派生 node.js EventEmitter 将在命名对象中调用错误的侦听器 属性

Derived node.js EventEmitter will call the wrong listener when in a named object property

我一直致力于使用一些 node.js 包(Express、mqtt、socket.io)和 MongoDB 数据库和 Angular 运行 在客户端。这个项目是我第一次与任何 JavaScript 合作,所以可以肯定地说我有点菜鸟。

我一直在解决派生 EventEmitter 在作为命名 属性 引用时调用错误侦听器函数的问题。这是一个演示问题的最小示例:

var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('test.js');
var util = require('util');

var TestEmitter = function (initialState, name) {
    var self = this;
    EventEmitter.call(self);
    this.state = initialState;
    this.name = name;
    this.setState = function (newState) {
        self.emit('change', newState, this.state);
        self.state = newState;
    };
};
util.inherits(TestEmitter, EventEmitter);

var myObj = {
    ary: [new TestEmitter(false, 'ary1'), new TestEmitter(true, 'ary2')],
    named: {
        name1: new TestEmitter(3, 'name1'),
        name2: new TestEmitter(4, 'name2')
    }
};

myObj.ary.forEach(function (aryEmitter) {
    aryEmitter.on('change', function (newState) {
        debug(aryEmitter.name + ' changed to: ' + newState);
    });
});
for (var prop in myObj.named) {
    var currEmitter = myObj.named[prop];
    debug('prop = ' + prop);
    debug('name = ' + currEmitter.name);
    currEmitter.on('change', function (newState) {
        debug(currEmitter.name + ' changed to: ' + newState);
    });
}

myObj.ary[0].setState(true);
myObj.ary[1].setState(false);
myObj.named.name1.setState(4);
myObj.named.name2.setState(5);

本质上,TestEmitter 是一个派生自 EventEmitter 的对象,它会在调用其 setState 方法时广播一个 change 事件。

myObj 是对四个 TestEmitter 实例的引用——两个在数组中,两个在命名属性中。在创建 myObj 之后,我为他们的每个 change 事件注册了一个侦听器,该侦听器使用调用回调的 TestEmitter 实例的名称简单地将调试输出写入控制台。

但是,命名的 TestEmitter 引用并不像我期望的那样工作。对 myObj.named.name1.setState(4)myObj.named.name2.setState(5) 的调用都将执行我为 EventEmitter myObj.named.name2 注册的回调函数。 运行 以上所有内容将产生以下输出:

test.js prop = name1 +0ms
test.js name = name1 +5ms
test.js prop = name2 +0ms
test.js name = name2 +0ms
test.js ary1 changed to: true +0ms
test.js ary2 changed to: false +0ms
test.js name2 changed to: 4 +0ms
test.js name2 changed to: 5 +0ms

有人可以提供帮助吗?我已经阅读了大量有关创建派生的最佳方法的文章 EventEmitters,看起来我采用了正确的方法,所以我有点难过。

感谢阅读以及您能提供的任何帮助!

你的问题变成了这部分代码:

for (var prop in myObj.named) {
    var currEmitter = myObj.named[prop];
    debug('prop = ' + prop);
    debug('name = ' + currEmitter.name);
    currEmitter.on('change', function (newState) {
        debug(currEmitter.name + ' changed to: ' + newState);
    });
}

因此当一个事件被触发时,在函数处理程序内部,currEmitter 具有 loop 的最后一个元素的值。

一个简单的解决方法是将它封装在一个函数中,创建一个新的作用域,然后在这个函数中做一些事情:

for (var prop in myObj.named) {
    var currEmitter = myObj.named[prop];
    (function(currEmitter){
      currEmitter.on('change', newState=>{
        debug(currEmitter.name + ' changed to: ' + newState);
      });
    })(currEmitter);
}

或者您可以使用 Object.keys():

Object.keys(myObj.named).forEach(function(key){
  var currEmitter = myObj.named[key];
  currEmitter.on('change', function(newState){
    debug(currEmitter.name + ' changed to: ' + newState);
  });
});