node.js入门 - 8.api:events
从今天开始,我们将介绍node的一些重要的api,首先向大家介绍的是events。他是node中相当重要的一个api,也是实现其他一些api的基础,对他有好的理解,会帮助你使用好其他的api。
如果你用javascript开发过浏览器的应用,那你一定很熟悉事件。但浏览器中的事件来源于dom,而不是javascript。dom中用户驱动的事件通过一系列树状元素(html/xml)的接口实现与用户的交互,当用户和dom交互的时候就会产生相应的事件。
1.EventEmitter
node中不存在dom这个对象,所以他自己创建了EventEmitter类来实现基本的事件功能,node中其他事件的实现都是通过这个接口类实现的。EventEmitter中最重要的两个方法是on和emit,来供其他类使用。on实现为事件实现监听的功能,它有两个参数,第一个是监听的事件,第二个是相应的回调函数。来看一个例子:
server.on('event', function(a, b, c) { //do things });
EventEmitter是一个接口,所以我们需要创建一个继承自它的类,使用新创建的类实现事件,而不是直接使用它。看下例:
var utils = require('utils'), EventEmitter = require('events').EventEmitter; var Server = function() { console.log('init'); }; utils.inherits(Server, EventEmitter);
var s = new Server(); s.on('abc', function() { console.log('abc'); });
通过上面的步骤,我们创建了Server类,并让它继承自EventEmitter,这样我们就可以使用EventEmitter中相应的方法了。例如上面我们使用on监听了一个叫做‘abc’的事件。那么我们如何才能触发这个事件呢?用下面的语句即可。
s.emit('abc');
这里需要强调的一点是,那些事件是实例级别的,不存在一个全局的事件,不同的Server的实例对象不能共享他们的事件。比如,上面的s对象就不能共享下面对象z的事件,
var z = new Server();.
2.Callback
使用事件,一个很重要的内容是使用回调函数,下面我们来了解下node中回调函数的机制。
在使用emit方法的时候,除了事件名称这个参数之外,我们还可以添加任意一些其他的参数,例如:
s.emit('abc', a, b, c);
其他的一些参数会传递给回调函数。当我们使用emit方法的时候,下面的代码会去访问每一个监听的事件。
if (arguments.length <= 3) { // fast case handler.call(this, arguments[1], arguments[2]); } else { // slower var args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); }
根据参数的长短,代码会选择是使用call还是使用apply。请注意第一个参数this,这意味着访问事件监听器的上下文是EventEmitter的上下文,而不是代码原先的上下文。使用node REPL,你会看到使用EventEmitter到底发生了什么。运行【开始】菜单,选择【所有程序】,找到【Node.js (x86)】,点【Node.js】,逐行输入下面内容(换行使用shift+回车),也可以使用粘贴复制的方式输入:
var EventEmitter = require('events').EventEmitter,util = require('util'); var Server = function() {}; util.inherits(Server, EventEmitter); Server.prototype.outputThis= function(output) { console.log(this); console.log(output); };
Server.prototype.emitOutput = function(input) { this.emit('output', input); }; Server.prototype.callEmitOutput = function() { this.emitOutput('innerEmitOutput'); };
var s = new Server(); s.on('output', s.outputThis); s.emitOutput('outerEmitOutput'); s.callEmitOutput(); s.emit('output', 'Direct');
结果:
注意到蓝线部分,这部分就是console.log(this)输出的内容,看的出this不论在那种情况下,指向的都是EventEmitter对象。