什么是事件总线、EventBus、EventEmitter ?
事件总线是一种设计模式,可以让不同的部件之间松散耦合地通信。一个事件总线通常由事件生产者和事件消费者组成。事件生产者会在事件总线上发布消息,而事件消费者则订阅这些消息,并在收到消息时执行相应的操作。
事件总线有很多方法, 这里我们只需要明白其中最核心的三个方法原理即可
第一个是 on 方法 监听一个事件
on(eventName, eventFn, thisArg) {
// 事件池中没有该事件 则创建一个 有则直接push 进去
this.eventMap[eventName] = this.eventMap[eventName] || [];
this.eventMap[eventName].push({
eventFn,
thisArg,
});
}
第二个是 off 方法, 取消事件监听
off(eventName, eventFn) {
if (!this.eventMap[eventName]) return;
// const newEventMap = [...this.eventMap[eventName]]; // 为了不影响原来的数组
// for (let i = 0; i < newEventMap.length; i++) {
// const fn = newEventMap[i];
// if (fn.eventFn === eventFn) {
// const index = this.eventMap[eventName].indexOf(fn);
// this.eventMap[eventName].splice(index, 1);
// }
// }
// 优化 filter不会改变原数组 会返回一个新数组
this.eventMap[eventName] = this.eventMap[eventName].filter(
(fn) => fn.eventFn !== eventFn
);
}
第三个方法, 发射一个事件 emit
emit(eventName, ...payload) {
if (!this.eventMap[eventName]) return;
this.eventMap[eventName].forEach((fn) =>
fn.eventFn.apply(fn.thisArg, payload)
);
}
完整代码
class MyEventBus {
constructor() {
// 事件池
this.eventMap = {}; // {abc: [fn1, fn2]}
}
on(eventName, eventFn, thisArg) {
// 事件池中没有该事件 则创建一个 有则直接push 进去
this.eventMap[eventName] = this.eventMap[eventName] || [];
this.eventMap[eventName].push({
eventFn,
thisArg,
});
}
off(eventName, eventFn) {
if (!this.eventMap[eventName]) return;
// const newEventMap = [...this.eventMap[eventName]]; // 为了不影响原来的数组
// for (let i = 0; i < newEventMap.length; i++) {
// const fn = newEventMap[i];
// if (fn.eventFn === eventFn) {
// const index = this.eventMap[eventName].indexOf(fn);
// this.eventMap[eventName].splice(index, 1);
// }
// }
// 优化 filter不会改变原数组 会返回一个新数组
this.eventMap[eventName] = this.eventMap[eventName].filter(
(fn) => fn.eventFn !== eventFn
);
if (this.eventMap[eventName].length === 0) {
delete this.eventMap[eventName];
}
}
emit(eventName, ...payload) {
if (!this.eventMap[eventName]) return;
this.eventMap[eventName].forEach((fn) =>
fn.eventFn.apply(fn.thisArg, payload)
);
}
}
使用方法
const eventZ = new MyEventBus();
const abc1 = function () {
console.log("监听abc1", this);
};
const abc2 = function () {
console.log("监听abc2", this);
};
const aliyu = {
name: "小鱼儿",
age: 18,
};
//使用
eventZ.on("abc", abc1, aliyu);
eventZ.on("abc", abc2, { name: "张三" });
function publish() {
eventZ.emit("abc");
}
document.querySelector("button").addEventListener("click", publish);
setTimeout(() => {
eventZ.off("abc", abc1);
eventZ.off("abc", abc2);
console.log("取消监听");
}, 3000);
EventEmitter 的缺点
- 如果一个事件有多个监听器,那么这些监听器会按照它们被添加的顺序依次执行。这可能导致一些问题,因为监听器可能会依赖于其他监听器的执行结果。
- 如果一个监听器抛出异常,则会导致整个事件处理过程被中断,剩余的监听器也不会再执行。
- EventEmitter 可能会出现内存泄漏的问题,如果一个对象持有了 EventEmitter,并且注册了很多监听器,但是却没有及时解除监听,那么这些监听器就可能会一直存在,导致内存泄漏。
- EventEmitter 的设计偏向于观察者模式,并不适用于所有场景。比如,在某些情况下,你可能希望消息能够被广播给所有监听器,而不是在第一个监听器中断事件处理过程之后就停止。
其实是可以有方法实现的,本人现阶段能力有限,如果有大佬实现,麻烦贴链接学习下,感谢感谢
尽管 EventEmitter 存在这些缺点,但是它仍然是一个很有用的工具,可以在适当的场景下使用。