js 发布订阅设计模式(观察者模式)

2020-01-19  本文已影响0人  看庭前花开花落_望天上云卷云舒
一、DOM0和DOM2的核心知识

1、语法上的区别

    box.onclick=function(){}
    box.addEventListener('click',function(){})

2、底层运行机制上的区别
DOM0就是给元素的某个属性绑定方法(有效绑定的方法只有一个)
DOM2是基于事件池机制完成,每增加一个绑定的方法,都会往事件池中存放一个...当事件触发会一次执行事件池中的事情
=》发布订阅其实就是模拟的事件池机制(可以给同一个元素的某个事件绑定多个不同的方法)
3、dom2中可以给一些特殊的事件类型绑定方法,这些事件类型DOM0不支持绑定,例如:DOMContentLoaded、transitionend...

$(document).ready()=>$(function(){})
vs
window.onload 
//二者的区别:
//前者相当于DOM2事件绑定,可以绑定多次 后者相当于dom0事件绑定 只能绑定一次;
//前者触发条件是dom加载完毕就开始触发 后者触发条件是dom结构和所有的元素都加载完成了开始触发

DOM2的事件池机制:
1、基于addEventListener/attachEvent(IE6-8)向事件池中追加方法:
新版本浏览器会根据元素和事件类型对新增的方法做重复校验,但是IE6-8不可以
2、当事件行为触发,会把事件池中的方法按照增加的顺序依次执行,但是IE6-8中执行的顺序不固定

二、jquery中发布订阅处理
<button class="submit">点我啊。。。</button>
let fn1=function(){
   console.log(1)
}
let fn2=function(){
   console.log(2)
}
let fn3=function(){
   console.log(3)
}
$('.submit').click(function(){
   fn1();
   fn2();
   fn3();
})
//1
//2
//3

三、基于ES6自己封装发布订阅库

//.构造函数模式
//.整体思路的实现
//.数组塌陷问题和解决办法

let _subscribe=(function(){
    //=>Sub:发布订阅类
    class Sub{
        //=>创建一个事件池,用来存储后期需要执行的方法
        constructor(){
            this.$pond=[];
        }
        //=>向事件池中追加方法(需要去重)
        add(func){
            //=>判断数组中是否存在相同的有相同的会返回来true
            let flag=this.$pond.some(item=>{
                return item===func
            });
            !flag?this.$pond.push(func):null;
        }
        //=>从事件池中移除方法:同时需要解决数据塌陷问题
        remove(func){
            let $pond=this.$pond;
            for(let i=0;i<$pond.length;i++){
                let item = $pond[i];
                if(item === func){
                    //=>移除(顺序不变的情况下基本只能用SPLICE了),但是不能这样写,这样会导致数组塌陷问题
                    //我们移除不能真移除,只能把当前项赋值未null,执行的时候我们再删除
                    //$pond.splice(i,1);
                    $pond[i]=null;
                    break;
                }
            }
        }
        //=>通知事件池中的方法,按照顺序依次执行,执行的时候还可以给每一个方法传递参数
        fire(...args){
            let $pond=this.$pond;
            for(let i=0;i<$pond.length;i++){
                let item=$pond[i];
                //让每一个方法中的this还是当前类的实例,
                if(typeof item !=='function'){
                    //=>此时再删除
                    $pond.splice(i,1);
                    i--;
                    continue;
                }
                item.call(this,...args);
            }
        }
    }
    //=>把Sub这个类暴露出去,new Sub()这样创建一个类的实例的时候就不用 new Sub()了,直接执行_subscribe
    return function subScribe(){
        return new Sub()
    }
})();

使用自己封装的方法库

<button class="submit">点我啊。。。</button>
let pond=_subscribe();
document.querySelector('.submit').onclick=function(ev){
    pond.fire(ev);
};
let fn1=function(){
    console.log(1)
}
let fn2=function(){
    console.log(2);
    pond.remove(fn1);
}
let fn3=function(){
    console.log(3)
}
let fn4=function(ev){
    console.log(4)
}
pond.add(fn1);
pond.add(fn2);
pond.add(fn3);
pond.add(fn4);

//打印结果:第一次点击结果:1 2 3 4
// 第一次点击结果:2 3 4

注:笔记源与珠峰培训

上一篇下一篇

猜你喜欢

热点阅读