最近看到过这样一个有意思的实现,使用 ES6 实现 EventBus。
题目的具体要求是这样的:
const e = new EventBus();
function fn1() {
console.log("fn1");
}
function fn2() {
console.log("fn2");
}
e.on("a", fn1);
e.once("a", fn2);
e.emit("a"); // fn1,fn2
e.emit("a"); // fn1
e.off("a", fn1);
e.emit("a"); // null
要求分析
- 支持四个方法:
on
,once
,emit
,off
; - 是按照添加的顺序依次执行方法;
once
添加的只会执行一次
简单实现
interface CallBackInfo {
(): void;
}
interface Listener {
cb: CallBackInfo;
options?: Options;
}
interface Options {
once?: boolean;
}
class EventBus {
private listener: { [key: string]: Listener[] } = {};
on(eventType: string, callback: CallBackInfo) {
this.add(eventType, callback);
}
once(eventType: string, callback: CallBackInfo) {
this.add(eventType, callback, { once: true });
}
private add(eventType: string, callback: CallBackInfo, options?: Options) {
if (!Array.isArray(this.listener[eventType])) {
this.listener[eventType] = [];
}
this.listener[eventType].push({
cb: callback,
options,
});
}
emit(eventType: string) {
const cbs = this.listener[eventType];
if (!Array.isArray(cbs) || cbs.length === 0) {
return null;
}
cbs.forEach((item) => {
if (item.options && item.options.once) {
this.off(eventType, item.cb);
}
item.cb();
});
}
off(eventType: string, callback: CallBackInfo) {
const cbs = this.listener[eventType];
if (!Array.isArray(cbs) || cbs.length === 0) {
return;
}
const curIndex = cbs.findIndex((item) => callback === item.cb);
if (curIndex >= 0) this.listener[eventType].splice(curIndex, 1);
}
}
const e = new EventBus();
function fn1() {
console.log("fn1");
}
function fn2() {
console.log("fn2");
}
e.on("a", fn1);
e.once("a", fn2);
e.emit("a"); // fn1,fn2
e.emit("a"); // fn1
e.off("a", fn1);
e.emit("a"); // null
使用示例运行后得到了期望的结果。