Demystifying events in node.js 阅读笔记
发布于 14 年前 作者 huangshaoqiang 6689 次浏览 最后一次编辑是 8 年前

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

7 回复

非常不错,抽空仔细看看。少强的学习开始上轨道了,其他同学要跟上!

呵呵,献丑了,算是科普读物吧

顶一个!this确实是个麻烦的东西

少强同学值得我们学习

不错,学习了。

顶,很想知道这个EventEmitter在node.js是怎么实现的?

var self = this; <br/>在前端javascript编程中经常用到,也有很多人用: <br/>var that = this; <br/>为了在闭包中去取到上下文的this对象。 <br/> <br/>另外楼主我有部分代码不明白: <br/>module.exports = Dummy; <br/> <br/>function Dummy() { <br/>events.EventEmitter.call(this); <br/>} <br/>我个人可以改写成这样的: <br/>module.exports = function(){ <br/>events.EventEmitter.call(this); <br/>} <br/>即将events.EventEmitter方法的运行环境指向module,这样写的目的是什么呢?

回到顶部