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);
}

// {
// gotSomthingFromInternet: [function(){}, function(){}]
// }

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 Emitter = require('events'); Node内置模块events

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);
};

util-inherits

使用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); // super constructor
// 确保从该函数构造器中创建出的对象继承所有EventEmitter的属性, 因为inherits只使对象继承prototype中的属性, 没有继承构造器本身的属性(下面有示例)
this.greeting = "Hello World!";
}

// 使Greetr继承EventEmitter的prototype的属性
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');

event-inherits

确保继承完整性

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() {
//Person.call(this);
this.badgenumber = '1234';
}

util.inherits(Policeman, Person);
var officer = new Policeman();
officer.greet();
// Hello undefined undefined

在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();
//Hello John Doe

使用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(); // 相当于Person.call(this)
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(); // 相当于EventEmitter.call(this)
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";
}
}

参考