likes
comments
collection
share

发布订阅

作者站长头像
站长
· 阅读数 36

发布订阅

所有的设计模式都是“锦上添花”;没有设计模式也可以实现需求,只不过基于设计模式可以更高效的管理代码!!

定义

发布订阅设计模式:

  • 制定一个计划表「创建一个容器」

  • 在没有通知计划执行之前,我们把后续要做的事情,逐一添加到计划表中!「添加到容器中」

  • 在指定阶段,通知计划表中的方法逐一执行即可!!

  • 也有人说,发布订阅就是模拟DOM2的事件池机制{只能处理浏览器标准事件},实现自定义事件的管理!!

原理

发布订阅是一种设计模式,基于事件池机制完成

  •  往事件池中添加事件 on [去重机制]

  •  从事件池中拿出事件来执行,fire(emit)

  •  从事件池中移除事件off/remove

详解

发布订阅 四个东西:一个对象(事件池),3个方法:订阅(增加)、发布(执行)、取消订阅(移除)

一个对象(事件池)

事件池这个对象可以有两种数据结构:

  •       是个数组,数组中的每一项是个对象,对象存储事件标志和事件方法

  •       是个对象,事件标记作为属性名,属性值是个数组,数组存储所有方法

 三个方法

  •      订阅(增加方法)on

    •  先判断事件池中有没有当前事件标志,没有就创建;有的话判断有没有绑定过这个方法,没有绑定的话再绑定(完成了去重)
  •      发布(执行方法)fire

    •   第一参数是事件标志,后面所有参数是给执行方法传参

    •   拿到传参的事件标志,把其中所有的方法强制改变this,并传参执行

  •       取消订阅(移除方法)off

    •   把某个事件标志中的某个方法移除

实现

Class实现

思路

  • 往事件池中添加事件

    • 先判断事件池中有没有相关属性,如果没有,需要赋值空数组

    • 判断数组中有没有当前方法

    • 往数组中添加方法

  • 从事件池中移除事件

    • 如果当前事件类型存在

    • 拿到当前项索引

    • 当索引>-1,就把这一项删除

  • 把事件池中的函数拿出来执行

    • 拿到事件池中每一个方法,改变this指向并传参执行

代码实现

class Subscribe {
    list = {};
    on(tag, fn) {        
        if (!this.list.hasOwnProperty(tag)) {
            this.list[tag] = [];
        } 
        if (this.list[tag].includes(fn)) return;       
        this.list[tag].push(fn);
    }   
    off(tag, fn) {     
        if (this.list[tag]) {          
            let i = this.list[tag].indexOf(fn);
            if (i > -1) {     
                this.list[tag].splice(i, 1);
            }
        }
    }   
    fire(tag, ...params) {
        if (this.list[tag]) {
            this.list[tag].forEach(fn => {             
                fn.call(this,...params);
            });
        }
    }
}

对象实现

思路

  • 全局只有一个事件池,是一个对象

  • 向事件池中添加方法

    • 事件池里如果还没有该标记

    • 就让该标记添加到事件池里,初始值为空数组

    • 重复的内容,不能添加到事件池里

  • 执行事件池中方法

    • 从事件池中取出标记值(数组)

    • 标记值,空数组,中断

    • 循环每一项

    • 不是函数

    • 删除元素,会有数组塌陷问题

  • 删除事件池中方法

    • 从事件池中取出标记值(数组)

    • 标记值,空数组,中断

    • 循环每一项

    • 数组塌陷 直接设置为null

  • 暴露API

  • 兼容浏览器

代码实现

发布订阅

(function(){
    let pond={};  
    const on=function on(tag,fn){      
        if(!pond.hasOwnProperty(tag)){
            pond[tag]=[];
        }        
        if(pond[tag].includes(fn)){return}
        pond[tag].push(fn);
        console.log(pond);
    }    
    const fire=function fire(tag,...params){
        let arr=pond[tag]||[];
        if(arr.length==0){return}
        let item,i;   
        for(i=0;i<arr.length;i++){
            item=arr[i];//f1,f2
            if(typeof item=="function"){
                item(...params);//f1(...params)
            }else{
                arr.splice(i,1);
                i--;
            }
        }
    }    
    const off=function off(tag,fn){
        let arr=pond[tag]||[];
        if(arr.length==0){return}
        let item,i;        
        for(i=0;i<arr.length;i++){
            item=arr[i];
            if(item==fn){
                //arr.splice(i,1);、
                arr[i]=null;
            }
        }
        
    } 
    let sub={
        on,
        fire,
        off
    }  
    if(typeof window!==undefined){
        window.sub=sub;
    }   
    //nodejs webpack
    if (typeof module === 'object' && typeof module.exports === 'object'){
        module.exports=sub;
    }
})()
转载自:https://juejin.cn/post/7176903865755762744
评论
请登录