为什么我需要在 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.js
和 tcp.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
有点特殊,因为它检查缺少的内部属性并在缺少时创建它们。因此,它会在您执行函数的任何上下文(全局或其他)上创建这些属性。
我的应用程序使用几种不同的机制与设备进行通信,例如串行(即像 USB CDC/虚拟 COM 端口)和 TCP(即像 telnet),我试图封装/隐藏/抽象这个功能一个更高级别的接口,以便使用这些接口的代码的其他部分不关心正在使用哪种机制。
在这种情况下,我有 serial.js
和 tcp.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
有点特殊,因为它检查缺少的内部属性并在缺少时创建它们。因此,它会在您执行函数的任何上下文(全局或其他)上创建这些属性。