发布订阅器的基本实现其实非常简单,核心思路就是维护一个对象,对象的key、value分别表示事件及其对应的回调函数数组。然后可以通过暴露的方法去清除事件对应的回调,也可以通过方法去触发事件对应的回调。
属性:消息队列
{
"click": [fn1, fn2, fn3],
"abc": [fnA, fnB]
}
/*
能向消息队列里添加内容 on
删除消息队列中的内容 off
触发消息队列内的内容 emit
*/
class Observer {
constructor(){
this.message = {}
}
// 添加事件与回调
on(type, fn){
if(!this.message[type]){
this.message[type] = []; // 效果同:this.message[type] = this.message[type] || [];
}
this.message[type].push(fn)
}
// 移除事件对应的回调
off(type, fn){
if(!this.message[type]) return;
if(!fn) {
delete(this.message[type]); // this.message[type] = undefined;
return;
}
this.message[type] = this.message[type].filter(fun => fun !== fn )
}
// 添加只允许被执行一次的事件
once(type, fn){
let onceFn = () => {
fn();
this.off(type);
}
this.on(type, onceFn);
}
// 触发事件对应回调
emit(type){
if(!this.message[type]) return;
this.message[type].forEach(item => item());
}
}
// ------------------------- 测试 -------------------------
function funSing(){
console.log('他会唱歌!');
}
function funDance(){
console.log('他还会跳舞!');
}
function funRap(){
console.log('他还会Rap??');
}
function funBasketball(){
console.log('嗯?他还会打篮球??');
}
let person1 = new Observer();
// 向person1委托一些内容,帮我观察
person1.on('singAndDance', funSing);
person1.on('singAndDance', funDance);
person1.once('Rap', funRap);
person1.off('singAndDance', funSing);
person1.emit('singAndDance');
person1.emit('singAndDance');
person1.emit('Rap');
person1.emit('Rap');
console.log( person1 );
完美运行 !
注意!触发完一次 Rap 对应的事件,message 中就不再存在 Rap 事件了,是因为我们在 once 中设定,执行完绑定的函数后,通过 off 删除对应的事件。