我们在之前的文章中讲解中发布订阅者模式,不太了解这种模式的,可以先点击查看之前的文章浅谈javascript设计模式之发布订阅者模式。从发布订阅者模式的思想出发,来实现一个EventEmitter模块。 在实现一些基础类库时,我们是经常需要用到 一个简单的EventEmitter模块,是只至少要实现emit和on两个方法: 这样就完成了一个简单的EventEmitter基类,这个基类可以自己单独使用: 也可以被其他类继承后使用: 基于 在如何实现once的方法时,必须得先知道事件是如何移除的: 通过Array.prototype.filter方法,过滤掉所有要移除的监听器。注意,跟为DOM添加和移除事件一样,不能传入匿名函数,例如下面的例子是不生效的,因为两个匿名函数虽然从函数体上看起来一样,但两者是不相等的: once,只要监听到第1个事件后,则立刻移除监听器,后续不再监听此事件: countdown事件会从10每秒输出一个数字,至到0为止。但我们使用once后,只输出了一个10后,则不再有后续的输出,说明监听器已经被移除。 这两者都是表示监听器有变化时,程序主动触发的: 那么这两个如何使用的呢,其实once这一个监听事件,就能触发上面的这两个监听器。先添加一个countdown监听器,执行一次监听后,马上移除countdown中的监听器: 我们从上面的章节中,已经了解了EventEmitter的原理,并实现了几个常用的模块。下面我们把完成的代码展示出来: EventEmitter功能我们经常有使用到,尤其是在实现的一些基础类库中,用到的更多。Vue中的bus功能也是这种思想。EventEmitter
模块的,比如在微信中下载某个APP时,前端需要了解当前下载处在哪个状态:刚开始下载、在在下载中、下载的进度、下载已完成、下载失败等等,都是需要我们向发布事件的。那么对应的就是实现emit
和on
。1. 简单的实现 #
class EventEmitter {
handles = {};
on(evName, listener) {
if (!this.handles[evName]) {
this.handles[evName] = [];
}
this.handles[evName].push(listener);
}
emit(evName, ...args) {
const handler = this.handles[evName];
if (handler) {
for(let i=0, len=handler.length; i
const downapp = new EventEmitter();
downapp.on('steps', step => console.log(step)); // 监听下载的进度
downapp.emit('steps', 0); // 输出: 0
downapp.emit('steps', 30); // 输出: 30
downapp.emit('steps', 100); // 输出; 100
class People extends EventEmitter {
constructor() {
super();
}
start() {
let num = 10;
let timer = setInterval(() => {
this.emit('countdown', num--);
if (num<=0) {
clearInterval(timer);
}
}, 1000)
}
}
const Tom = new People();
Tom.on('countdown', num => console.log('Tom', num));
Tom.start();
const Jerry = new People();
Jerry.on('countdown', num => console.log('Jerry', num));
Jerry.start();
2. 对EventEmitter扩展方法 #
emit
和on
我们可以扩展出多个方法,方便使用:
方法名
说明
on(eventName, listener)
监听所有触发的事件
emit(eventName, ...args)
主动触发事件
once(eventName, listener)
与
on
事件类似,但只监听一次事件
off(eventName, listener)
移除某个事件
offAll([eventName])
移除所有的事件,若传入具体的eventName,则移除该名称下所有的事件
newListener
有添加新的监听器时触发
removeListener
有监听器被移除时触发
2.1 off #
class EventEmitter {
off(evName, listener) {
if (this.handles[evName]) {
// 从数组中移除lisnter
this.handles[evName] = this.handles[evName].filter(fn => fn!==listener);
}
}
}
// 接上面的People类
// const Tom = new People();
const listener = (num) => {
console.log('Tom', num)
}
const listener2 = (num) => {
console.log('Tom', num*num);
}
Tom.on('countdown', listener);
Tom.on('countdown', listener2);
Tom.on('countdown', listener);
Tom.off('countdown', listener); // 移除所有的listener
Tom.start(); // 只有listener有输出
Tom.on('countdown', (num) => {console.log(num)});
Tom.off('countdown', (num) => {console.log(num)}); // 移除
Tom.start(); // 依然有输出
2.2 once #
Tom.once('countdown', (num) => {console.log(num)}); // 10
Tom.start();
2.3 newListener和removeListener #
class EventEmitter {
on(evName, listener) {
if (!this.handles[evName]) {
this.handles[evName] = [];
}
this.handles[evName].push(listener);
// 有新增事件时,触发newListener
this.emit('newListener', evName, listener);
return this;
}
off(evName, listener) {
if (this.handles[evName]) {
// 从数组中移除lisnter
this.handles[evName] = this.handles[evName].filter(fn => {
if (fn!==listener) {
// 监听器有移除时,触发removeListener
this.emit('removeListener', evName, listener);
return true;
}
return false;
})
}
}
}
const listener = (num) => {
console.log('Tom', num)
}
Tom.on('newListener', (evName, listener) =>
console.log('newListener', evName, listener)
);
Tom.on('removeListener', (evName, listener) =>
console.log('removeListener', evName, listener)
);
Tom.once('countdown', listener);
Tom.start();
3. 完整的EventEmitter模块 #
class EventEmitter {
handles = {};
on(evName, listener) {
if (!this.handles[evName]) {
this.handles[evName] = [];
}
this.handles[evName].push(listener);
this.emit('newListener', evName, listener);
return this;
}
once(evName, listener) {
let fired = false;
let magic = (...args) => {
this.off(evName, magic);
if (!fired) {
fired = true;
listener.apply(this, args);
}
}
this.on(evName, magic);
return this;
}
off(evName, listener) {
if (this.handles[evName]) {
// 从数组中移除lisnter
this.handles[evName] = this.handles[evName].filter(fn => {
if (fn!==listener) {
this.emit('removeListener', evName, listener)
return true;
}
return false;
})
}
}
emit(evName, ...args) {
const handler = this.handles[evName];
if (handler) {
for(let i=0, len=handler.length; i
4. 总结 #
版权属于:
加速器之家
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论