Node中很多模块都运用了Event Emitter, 以下是对Event Emitter的介绍及在Node中的实现方式, 还介绍了在JavaScript中实现继承的几种方式.
Node中的事件
Node需要处理两种类型的事件.
1.系统事件(System Events)(C++ Core [libuv])
It comes from the c++ side of node.js core, thanks to a library called libuv.
操作系统相关的事件, 例如:
2.定制事件(Custom Events)(JavaScript core [Event Emitter])
When a event occurs in libuv, it generates a Custom JavaScript event to make it easier for us to decide what code should run when that event happens.
Actually JavaScript is faking event because it does not have event object.
EventEmitter的实现原理(简化版)
emitter.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function Emitter(){ this.events = {}; }
Emitter.prototype.on = function(type, listener){ this.events[type] = this.events[type] || []; this.events[type].push(listener); }
Emitter.prototype.emit = function(type){ if(this.events[type]) { this.events[type].forEach(function(listener){ listener(); }) } }
module.exports = Emitter;
|
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var Emitter = require('./emitter');
var emtr = new Emitter();
emtr.on('greet', function(){ console.log('Somewhere, someone said hello.'); });
emtr.on('greet', function(){ console.log('A greeting occurred!'); }); emtr.emit('greet');
|
Magic String
代码中含有特殊含义的字符串, 传入emit和on方法中的第一个参数就是Magic String, 上例中为’greet’. 直接以字符串的形式传入会使调试变困难.
可以这样解决该问题(不容易犯错, 更易控制):
添加一个配置文件config.js
:
config.js
1 2 3 4 5 6 7
| module.exports = { events: { GREET: 'greet', FILESAVED: 'filesaved', FILEOPENED: 'fileopened' } }
|
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var Emitter = require('events'); var eventConfig = require('./config').events;
var emtr = new Emitter();
emtr.on(eventConfig.GREET, function(){ console.log('Somewhere, someone said hello.'); });
emtr.on(eventConfig.GREET, function(){ console.log('A greeting occurred!'); }); emtr.emit('greet');
|
从EventEmitter继承
继承的方法inherits实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| exports.inherits = function(ctor, superCtor) {
if (ctor === undefined || ctor === null) throw new TypeError('The constructor to "inherits" must not be ' + 'null or undefined');
if (superCtor === undefined || superCtor === null) throw new TypeError('The super constructor to "inherits" must not ' + 'be null or undefined');
if (superCtor.prototype === undefined) throw new TypeError('The super constructor to "inherits" must ' + 'have a prototype');
ctor.super_ = superCtor; Object.setPrototypeOf(ctor.prototype, superCtor.prototype); };
|

使用inherits继承EventEmitter
Node中许多内置对象都从EventEmitter继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var EventEmitter = require('events'); var util = require('util');
function Greetr(){ EventEmitter.call(this); this.greeting = "Hello World!"; }
util.inherits(Greetr, EventEmitter); Greetr.prototype.greet = function(data){ console.log(this.greeting + ': '+ data); this.emit('greet',data); }
var greeter1 = new Greetr();
greeter1.on('greet', function(data){ console.log('Someone greeted!: ' + data); });
greeter1.greet('Tony');
|

确保继承完整性
inherits只使对象继承prototype中的属性, 没有继承构造器本身的属性, 利用构造器.call(this)
确保继承完全.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var util = require('util');
function Person() { this.firstname = 'John'; this.lastname = 'Doe'; }
Person.prototype.greet = function() { console.log('Hello ' + this.firstname + ' ' + this.lastname); }
function Policeman() { this.badgenumber = '1234'; }
util.inherits(Policeman, Person); var officer = new Policeman(); officer.greet();
|
在Policeman构造器中调用Person.call(this);
后, 从Policeman中实例化出的对象的属性就不只继承自原型链, 还有Person
构造器中定义的属性, 这样再调用office.greet()
, 结果就是: `Hello John Doe’.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var util = require('util');
function Person() { this.firstname = 'John'; this.lastname = 'Doe'; }
Person.prototype.greet = function() { console.log('Hello ' + this.firstname + ' ' + this.lastname); }
function Policeman() { Person.call(this); this.badgenumber = '1234'; }
util.inherits(Policeman, Person); var officer = new Policeman(); officer.greet();
|
使用ES6 class重构上述代码
class只是语法糖, 并不是其他编程语言中的”类”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Person { constructor(firstname, lastname){ this.firstname = 'John'; this.lastname = 'Doe'; } greet(){ console.log(`Hello ${this.firstname} ${this.lastname}`); } }
class Policeman extends Person { constructor( ){ super(); this.badgenumber = '1234'; } } var officer = new Policeman(); officer.greet();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var EventEmiiter = require('events');
class Greetr extends EventEmiiter { constructor(){ super(); this.greeting = "Hello World!"; } greet(data){ console.log(`${this.greeting}: ${data}`); this.emit('greet', data); } }
var greeter1 = new Greetr(); greeter1.on('greet', (data) => { console.log(`Someone greeted!: ${data}`); });
greeter1.greet('Tony');
|
class使用module.exports:
1 2 3 4 5 6 7 8
| var EventEmitter = require('events');
module.exports = class Greetr extends EventEmiiter { constructor(){ super(); this.greeting = "Hello world"; } }
|
参考