事件订阅的编程方式

到 node 中的事件订阅器的启发,我感觉事件编程在某些地方很有用。尤其是解耦方面,于是乎,造轮子事件来了。

还记得前端网页是怎么注册浏览器事件的吗?

1
2
3
4
5
6
var a = document.createElement('a');
a.addEventListener('click', function () {
alert('hello, you clicked');
});

其实这就是事件订阅了。因为 a 注册了 click 事件,并绑定了回调函数。当 a 被鼠标所点击的时候,这个回调函数就会被执行。

那么怎么样做一个无关平台的 javascript 事件订阅器呢?其实也挺容易的,一个完整的事件订阅器应该由这几个部分组成:

  • 能注册事件
  • 能删除事件
  • 能产生事件并执行订阅者指定的回调函数

这几部分理清楚了就好设计了

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
26
27
class EventEmitter {
checkPool(name){
if (typeof(this._evpool) !== 'object') {
this._evpool = {}
return this.checkPool(...arguments)
}
if (!Array.isArray(this._evpool[name])) {
this._evpool[name] = []
return this.checkPool(...arguments)
}
return this._evpool[name]
}
emit(name, ...args){
this.checkPool(name).forEach(cb => cb(...args))
}
on(name, ...args){
this.checkPool(name).push(...args)
}
remove(name, ...args){
this._evpool[name] = this.checkPool(name).filter(cb => !args.includes(cb))
}
clear(name){ this.checkPool(name).splice(0) }
}

这个自制的事件订阅器有注册事件(on)、产生事件(emit)、删除事件函数(remove)以及移除事件所有函数(clear)的功能。用法也还好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const people = {
name: 'vec',
run(){
this.emit('run', this.name);
},
};
/* 必须要获得这个事件订阅器的各种方法 */
people.__proto__ = EventEmitter.prototype;
people.on('run', name => console.log(name, '在跑步'));
people.run();
//vec 在跑步

当然,这个只是很简单的事件订阅器,稍微能看的应该是要支持 冒泡绑定上下文 的,不过对于凑稿费和加深理解应该够用了