订阅发布模式
也叫观察者模式
案例
在vue中,多处用到了订阅发布模式, 其中,组件通信的一种方式,eventBus就是用的订阅发布模式。
eventBus
class EventBus{
constructor(){
// 在event对象中 存放 所有的事件与回调数组,如:
// {a: [ ()=>{console.log(1)}, (a)=>{console.log(a)}], b: [()=>{console.log(1)}]}
this.event=Object.create(null);
};
/**注册事件
* @输入 事件名 name , 对应的回调函数。
* @输出 undefined
* @功能 往event对象中加入事件与回调数组。
*/
on(name,fn){
if(!this.event[name]){
//一个事件可能有多个监听者
this.event[name]=[];
};
this.event[name].push(fn);
};
/**触发事件
* @输入 事件名 name , 剩余参数(触发的事件名对应的回调数组中,回调函数的参数(不定参))。
* @输出 undefined
* @功能 触发事件名对应的回调数组
*/
emit(name,...args){
//遍历要触发的事件对应的数组回调函数。依次调用数组当中的函数,并把参数传入每一个cb。
this.event[name] && this.event[name].forEach(fn => {
fn(...args)
});
};
/**只触发一次事件
* @输入 事件名 name ,回调函数fn
* @输出 undefined
* @功能 借助变量cb,同时完成了对该事件的注册、对该事件的触发,并在最后取消该事
*/
once(name,fn){
var cb=(...args)=>{
//触发
fn(...args);
//取消
this.off(name,fn);
};
//监听
this.on(name,cb);
};
/**取消事件
* @输入 事件名 name ,要取消的对应的回调
* @输出 undefined
* @功能 删除指定事件名,回调数组中的某一个回调。
*/
off(name,offcb){
if(this.event[name]){
// 找到要取消事件在回调数组中的索引
let index=this.event[name].findIndex((fn)=>{
return offcb===fn;
});
//通过索引删除掉对应回调数组中的回调函数。
this.event[name].splice(index,1);
// 回调数组长度为0时(没有回调数组时)
if(!this.event[name].length){
// 删除事件名
delete this.event[name];
}
}
}
}
实例化
var a = new EventBus()
// on emit
a.on('a',()=>{console.log(1)});
a.on('a',(a)=>{console.log(a)});
a.on('b',()=>{console.log(1)});
a.emit('a','2'); // 1 2
a.emit('b'); // 1
//此时的event对象是
console.log(a.event);
// {a: [ ()=>{console.log(1)}, (a)=>{console.log(a)}], b: [()=>{console.log(1)}] }
// once
var fnc = (a)=>{console.log(a)};
a.once("d",fnc(1))
// off
var fnc = (a)=>{console.log(a)};
a.on('c',fnc);
a.event; // {a: Array(2), b: Array(1), c: Array(1), d: Array(2)}
a.off("c",fnc);
a.event; // {a: Array(2), b: Array(1), d: Array(2)}
双向数据绑定
也是通过订阅发布模式来实现的。当然还有关键的数据劫持Object.defineProperty
待补充
总结
- 我们的event对象,管理所有的事件以及对应的回调数组。也可以用new Map来管理更加方便一些
- 整个订阅发布模式,就是往event对象中添加事件与回调数组。并且通过事件名,调用对应回调数组中的函数。
- 我们的案例,on , emit ,once , off 都必须传入 事件名和回调函数名来执行。