订阅发布模式,了解一下?

522 阅读2分钟

订阅发布模式

也叫观察者模式

案例

在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 都必须传入 事件名和回调函数名来执行。