JS实现发布/订阅
最近在看面试题时,遇到了手写发布/订阅的题目,所以想写一个文档进行总结。
大体上的思路是通过在class
中创建一个存储事件及其关联函数的对象,使用on
方法进行事件的存储,将传入的函数保存到相应名称的属性中;使用emit
方法触发事件,执行对象中相应属性中的函数;使用remove
方法移除事件,移除相应属性中的函数或直接移除相应属性。
class MyEvent {
constructor() {
this.eventList = {};//创建事件对象,每个属性都是一个数组,可以存放多个函数
}
on(name, fn) {//创建/添加事件
if (!this.eventList[name]) {
this.eventList[name] = [];//如果没有该事件,就创建并初始化该事件
}
if (typeof fn !== "function") throw new Error("缺少回掉函数");
this.eventList[name].push(fn);//将回掉函数添加到该事件的函数队列中去
}
emit(name, ...param) {//触发事件
if (!this.eventList[name]) {//如果不存在该事件的话就报错
throw new Error(`未知事件${name}`);
}
this.eventList[name].forEach((fn) => {//否则就挨个执行该事件中的函数,并将参数传进去
fn(...param);
});
}
remove(name, fn) {//移除事件
if (!this.eventList[name]) {//如果不存在该事件的话就报错
throw new Error(`未知事件${name}`);
}
if (!fn) {//如果没有传第二个参数则默认删除该事件
delete this.eventList[name];
} else if (typeof fn !== "function") throw new Error("函数错误");//如果传入类型错误则报错
else {
const index = this.eventList[name].indexOf(fn);//查找函数在该事件函数队列中的下标
if (index === -1) throw new Error("未找到函数");//如果没找到就报错
this.eventList[name].splice(index, 1);//找到了就删除该函数
if (this.eventList[name].length === 0) delete this.eventList[name];//如果该事件的函数队列为空,则删除该事件
}
}
}
在使用时可以通过new
来创建实例,调用其中的方法进行发布/订阅。
const callbackfunc1 = (a) => {
console.log("触发事件", a);
};
const callbackfunc2 = () => {
console.log("触发事件2");
};
let eventTest = new MyEvent();
eventTest.on("event1", callbackfunc2);
eventTest.emit("event1", 11111);
eventTest.remove("event1", callbackfunc1);
eventTest.emit("event1", 11111);
eventTest.remove("event1");
需要注意的是,如果事件绑定的函数之后可能要被移除,那么该函数要提前定义好。如果在传入的时候再进行定义,移除时使用indexOf()
方法会找不到该函数,因为在使用on
创建和使用remove
移除时,对程序来说传入的实际上是两个不同的函数:
eventTest.on("event1", (a) => {
console.log("触发事件", a);
});
eventTest.emit("event1", 11111);
eventTest.remove("event1", (a) => {//这里会报错,找不到函数
console.log("触发事件", a);
});
标题复制10行,并且每行大于10个字符
标题复制10行,并且每行大于10个字符
标题复制10行,并且每行大于10个字符
标题复制10行,并且每行大于10个字符
标题复制10行,并且每行大于10个字符
标题复制10行,并且每行大于10个字符
标题复制10行,并且每行大于10个字符