手写EventBus 作为经典的前端手写面试题,在工作之余我觉得还是得记录一下😊 什么是EventHub
EventHub是基于发布订阅模式实现的一个实例, 翻译为事件中心,常用于多模块(组件)间的通信。
今天我们就简单的实现EventHub的发布、订阅和取消订阅用一个简单的例子-定闹钟:
首先我们确定Api:
- on 订阅(打开某时的闹钟开关)
- emit 发布(时间到了,大懒猪快起床)
- off 取消订阅(关闭闹钟开关)
实现发布和订阅
我们得用一个变量eventMap来暂存事件名字(比如:闹钟)和事件(比如铃声响),这里我们用一个数组来记录事件(因为考虑多次订阅):
class EventHub {
private eventMap: { [key: string]: Array<(params?) => void> } = {};
on(eventName, fn) {}
emit(eventName) {}
off(eventName) {}
}
export default EventHub;
当我们订阅一个事件的时候,我们把这个事件push到订阅的东西的事件队列中,比如我在手机闹钟里面订了个3秒后的闹钟:
import EventHub from "../src/index";
const eventHub = new EventHub();
eventHub.on("闹钟", () => {
setTimeout(() => {
console.log("闹钟响了");
}, 3000);
});
eventHub.emit("闹钟");
然后我们在类里面的on函数把事件push进去:
class EventHub {
private eventMap: { [key: string]: Array<(params?:any) => void> } = {};
on(eventName: string, fn: (params?: any) => void) {
this.eventMap[eventName] = this.eventMap[eventName] || [];
this.eventMap[eventName].push(fn);
}
emit(eventName) {}
off(eventName) {}
}
export default EventHub;
我们在emit触发执行逻辑:
class EventHub {
private eventMap: { [key: string]: Array<(params?: any) => void> } = {};
on(eventName: string, fn: (params?: any) => void) {
this.eventMap[eventName] = this.eventMap[eventName] || [];
this.eventMap[eventName].push(fn);
}
emit(eventName: string) {
(this.eventMap[eventName] || []).forEach((fn) => fn());
}
off(eventName: string) {
console.log(eventName);
}
}
export default EventHub;
执行触发的代码,看控制台(提示:node 运行ts文件请下载ts-node)
实现取消订阅:
当我们在emit 之前 off闹钟的订阅
import EventHub from "../src/index";
const eventHub = new EventHub();
const fn = () => {
setTimeout(() => {
console.log("闹钟响了");
}, 3000);
};
eventHub.on("闹钟", fn);
eventHub.off("闹钟", fn);
eventHub.emit("闹钟");
加上取消订阅的逻辑:
off(eventName: string, fn: (params?: any) => void) {
const index = this.eventMap[eventName].findIndex((item) => item === fn);
if (index === -1) return;
this.eventMap[eventName].splice(index, 1);
console.log("取消闹钟成功");
}
执行取消成功: