刚开始学习nodejs时曾被node中的事件困扰,在查阅了一些文章之后迷惑才得以解开,想到以后也许还会有人遇到同样的问题,于是将学习过程记录下来。自己的功底不够,希望能对初学者有所帮助吧。
在 nodejs 下编程,事件是必不可少的,异步回调的时候经常要用到。但是系统预定义的事件有限,有时候程序一复杂就满足不了我们的要求了,这时候我们就需要自定义一些事件。
关于nodejs的事件,Node的文档中有很多示例,但如何编写自定义事件以及监听函数,文档中就没有清晰的描述了。幸亏有 Demystifying events in node.js 这篇文章,文中以一个简洁的例子介绍了为一个对象添加事件及监听函数的流程,文章不错,我也是在这篇文章中找到的答案。 不过遗憾的是文中给出的代码例子不能正常运行,也没有对代码做出相应解释,容易给读者造成一些迷惑的地方。下面我就做一些补遗的工作。
给对象添加自定义事件和监听函数,会有 3 个方面的工作要完成:
首先,如文档所描述的,对象必须继承自 events.EventEmitter
接着,在对象上注册事件监听函数。
最后,在特定条件下触发事件。
1.继承自 events.EventEmitter
文档中有提到:All objects which emit events are instances of events.EventEmitter.
继承自 events.EventEmitter 的对象将具有注册事件和触发事件的功能。下面的代码具体实现的就是完成对其的继承:
// basic imports
var events = require(‘events’);
// for us to do a require later
module.exports = Dummy;
function Dummy() {
events.EventEmitter.call(this);
}
// inherit events.EventEmitter
Dummy.super_ = events.EventEmitter;
Dummy.prototype = Object.create(events.EventEmitter.prototype, {
constructor: {
value: Dummy,
enumerable: false
}
});
上面代码中继承有两部分,一部分是代码下面那部分:原型继承,把Dummy挂载到 events.EventEmitter的原型链上;这样Dummy才能具备所有“祖先”的功能和属性。
再一部分是Dummy函数,重点在于call,其实call在这里的作用就是以this(指向Dummy)作为运行环境来执行 events.EventEmitter这个构造函数。这样就等同于构造了和events.EventEmitter 一模一样的 Dummy了。
另外,其实node的sys模块已经有了这个函数,在 NODE_SOURCE_ROOT /lib/sys.js 第386行(node 版本 0.2.6).我们在require(‘sys’)之后可以直接使用。
var sys = require(‘sys’);
sys.inherits(Dummy, events.EventEmitter);
2.触发事件
Dummy.prototype.cooking = function(chicken) {
var self = this;
self.chicken = chicken;
self.cook = cook(); // assume dummy function that’ll do the cooking
self.cook(chicken, function(cooked_chicken) {
self.chicken = cooked_chicken;
self.emit('cooked’, self.chicken);
});
return self;
}
在这个函数中,有一个特别的地方,作者没有直接的使用this,而是把它赋值给变量self,然后再通过self来引用this。
初看这个地方会觉得奇怪,这样做有必要吗?另外容易被self迷惑,以为self是关键字什么的,其实self只是个变量名而已,换成foo什么的都没有问题。
问题的关键在于self出现在两个地方:cook函数内和cook函数外,并且javascript中的this比较特殊,它并不是一成不变的,它所引用的对象会随着运行的上下文改变而改变。为了在cook函数中还能引用到函数 cooking,所以这里先将this保存到了变量self中。 详细了解可以参考这篇文章:What is “this”?
3.注册事件监听函数
// A nonsensical node.js program
var Dummy = require(‘./dummy’);
var kenny = new Dummy();
var dinner = kenny.cooking(fried_chix);
kenny.on('cooked’, function(chicken) {
// eat up!
});
上面的代码是为 kenny添加cooked事件的监听函数,但作者在cooking函数被调用后才开始对事件响应函数的注册,这样就可能产生问题:如果cooking函数在注册listener之前就已经执行完毕,那cooked事件就不会被触发了。
var self = this;
在前端javascript编程中经常用到,也有很多人用:
var that = this;
为了在闭包中去取到上下文的this对象。
另外楼主我有部分代码不明白:
module.exports = Dummy;
function Dummy() {
events.EventEmitter.call(this);
}
我个人可以改写成这样的:
module.exports = function(){
events.EventEmitter.call(this);
}
即将events.EventEmitter方法的运行环境指向module,这样写的目的是什么呢?