跳到主要内容

实现一个EventEmitter发布订阅

  • 组件通信:发布订阅模式可以在不同组件之间通过事件来通信
  • 模块通信:不同模块之间需要进行通信

实现思路

  • 发布订阅核心,是事件注册on/once、事件触发 emit 方法、
    • on 方法用于注册事件,它接收事件名和回调函数作为参数,将回调函数添加到对应事件的回调列表中。
    • once 方法也是注册事件的一种,但是它只会触发一次,即在第一次触发事件时执行完回调后会自动移除监听。
  • 定义一个events属性,存储事件的,这个events数据结构是对象,key表示时间名,value是数组方法,因为事件可能被添加多次回调
  • off 方法用于移除事件监听器
js
//发布订阅

class EventEmitter {
constructor() {
this.events = {};
/*
events对象用来存储事件
events = {
一个事件会可能多个回调 (一个事件可能会被添加多次回调)
add:[fn,fn1,fn2],
delete:[fn,fn1,fn2]

}
*/
}
// 触发event事件,并把参数arg1,arg2,arg3....传给事件处理函数;
emit(event, ...args) {
const cbs = this.events[event];
if (!cbs) {
console.log(`${event} 没有被注册`);
return this;
}
//同步代码,遍历去执行
//使用apply为了改变this,也更好的传递参数给函数去调用
cbs.forEach((cb) => cb.apply(this, args));
return this;
}
//监听event事件,事件触发时调用fn函数
on(event, cb) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(cb);
//如果希望是链式调用的话,返回this
return this;
}
//【注册一次事件】为指定事件注册一个单次监听器,单次监听器最多只触发一次,触发后立即解除监听器;
once(event, cb) {
const func = (...args) => {
//执行回调之前,先移除事件
this.off(event, func);
cb.apply(this, args);
};
this.on(event, func);
return this;
}
//停止监听某个事件,事件的解除
//传2个参数,第二个参数可以指定移除哪个回调
off(event, cb) {
//如果没有传cb,则移除这个事件所有回调
if (!cb) {
this.events[event] = null;
} else {
this.events[event] = this.events[event].filter((item) => item !== cb);
}
console.log(this.events);
return this;
}
}

const ev = new EventEmitter();
ev.once("once", (v) => {
console.log(v);
});

ev.emit("once", "test1");
ev.emit("once", "test2");

实现最大监听数【扩展】​

  • 限制最大监听数,不希望有太多的监听,消耗太多内存
  • 关键代码
    • 1.加一个属性maxListeners表示最大监听数
    • 2.在on方法里,进行判断,如果大于监听数,则就返回
text
const ev = new EventEmitter({maxListeners:3});
js
class EventEmitter {
constructor(options) {
this.events = {};
//Infinity 表示无穷大,如果没有maxListeners,则监听数不做限制
this.maxListeners = options.maxListeners || Infinity;
}
on(event, cb) {
if (!this.events[event]) {
this.events[event] = [];
}
if (
this.maxListeners !== Infinity &&
this.events[event].length >= this.maxListeners
) {
console.warn(`${event} 超过最大监听数!`);
return this;
}
this.events[event].push(cb);
//如果希望是链式调用的话,返回this
return this;
}