为什么我需要在 passing/exposing 时包装我的 EventEmitter 的 on 函数?

Why do I need to wrap my EventEmitter's on function when passing/exposing it?

我的应用程序使用几种不同的机制与设备进行通信,例如串行(即像 USB CDC/虚拟 COM 端口)和 TCP(即像 telnet),我试图封装/隐藏/抽象这个功能一个更高级别的接口,以便使用这些接口的代码的其他部分不关心正在使用哪种机制。

在这种情况下,我有 serial.jstcp.js,它们各自导出一个名为 connect 的函数,return 是一个具有 on: function() { ... } 属性,我想连接到他们的内部 EventEmitter 实例。 (我不认为我应该公开整个对象,因为这可能允许其他代码调用发射并产生难以调试的问题。)

让我感到困惑的是,如果我只是 return { on: emitter.on } 那么回调似乎永远不会被调用,但是如果我 return { on: function(a, b) { return emitter.on(a,b); } 它工作正常。我觉得这与闭包/范围或解析符号 emitter 的时间有关,但这与我之前遇到的有关这些主题的其他麻烦不同。有人能帮我理解是什么让这两行相似的代码如此不同吗?

"use strict";

const net = require('net');
const EventEmitter = require('events');
const ConnectionEventNames = require('./events.js');


function connect(settings) {
    // ... (validates settings) ...
    const emitter = new EventEmitter();

    const socket = net.connect({
        host: settings.host,
        port: settings.port
    }, () => {
        emitter.emit(ConnectionEventNames.connected);
    });

    socket.on('data', (data) => {
        emitter.emit(ConnectionEventNames.received_data, data);
    });
    socket.on('error', (error) => {
        emitter.emit(ConnectionEventNames.error, error);
    });
    socket.on('end', () => {
        emitter.emit(ConnectionEventNames.disconnected);
    });
    socket.setNoDelay(settings.setNoDelay);

    return {
        // FIXME: why didn't this work the way I initially wrote it? (next line)
        on_original: emitter.on,
        on: function(eventName, listener) {
            return emitter.on(eventName, listener);
        },
        // ...
    };
}


module.exports = {
    connect
};

像那样传递实例的原型函数时的问题是上下文丢失了。 emitter.on 没有隐式绑定到 emitter,它只是一个普通函数。如果没有 emitter 上下文,.on() 实现中的 this 将不会指向您所期望的内容。

通常您会很快看到异常抛出,因为大多数原型函数通常假定一个有效的上下文,因此尝试访问 this 上的特定属性,但是 EventEmitter 有点特殊,因为它检查缺少的内部属性并在缺少时创建它们。因此,它会在您执行函数的任何上下文(全局或其他)上创建这些属性。