javaScript发布订阅模式

时间:2024-03-01 15:41:07

1,什么是发布订阅模式  

        发布订阅模式其实是一种对象之间一对多的依赖关系(利用消息队列)。当一个对象的状态发生改变时,所有依赖它的对象都得到状态的改变的通知。订阅者(subscriber)把自己想订阅的事件注册到调度中心(Event Channel),当发布者(Publisher)发布该事件(publish Event)到调用中心,也就是该事件触发时,由调度中心统一调度(Fire Event) 订阅过者注册到调度中心处理代码。

完成整个发布订阅流程一共有3个

一,发布者

二,事件中心

三,订阅者

个人理解:有很多学生(相当于订阅者)需要收到学校的通知,但是需要先进老师创建的群(相当于事件中心)。当老师(发布者)在群里发送群消息的时候,以群消息的形式通知所有的学生,这时所有的学生都能收到消息。

2,如何实现发布订阅模式

基本思路

1,创建一个对象
2,创建一个缓存列表相当于(调度中心)
3,创建on方法用来把函数fn都添加到缓存列表中
4,创建emit方法取到arguments里第一个当做event,根据event值去执行对应缓存列表中的函数(发布都发布事件到调度中心,调度中心去进行对应的处理)

实现1

<script>
    //可以理解为调度中心
    let ObjPublisher={}
    //缓存列表
    ObjPublisher.list={}
    console.log(ObjPublisher)
    //进行订阅
    ObjPublisher.on=function(event,fn){
        let that = this;
        //如果对象中没有对应的event值,也就是没有订阅过,就给event创建一个缓存列表
        //如果对象中有相应的event值,把fn添加到对应的event的缓存列表里
        (that.list[event]||(that.list[event]=[])).push(fn)
        return that
    }
    //发布
    ObjPublisher.emit=function () {
        let that = this;
        //第一个参数是对应的event值,直接用数组的shift方法取出
        let event=[].shift.call(arguments),fns=[...that.list[event]]
        //如果返回列表里面没有nf就返回false
        if(!fns||fns.length===0){
            return false
        }
        //遍历event值对应的缓存列表,依次执行fn
        fns.forEach(fn=>{
            fn.apply(that,arguments)
        })
        return that
    }
    function user1(content) {
        console.log("张三",content)
    }
    function user2(content) {
        console.log("李四",content)
    }
    function user3(content) {
        console.log("王五",content)
    }
    function user4(content) {
        console.log("赵六",content)
    }
    //订阅
    ObjPublisher.on('asdf',user1)
    ObjPublisher.on('asdf',user2)
    ObjPublisher.on('jkl',user3)
    ObjPublisher.on('jkl',user4)
    //发布
    ObjPublisher.emit('asdf','js发布订阅模式')
    ObjPublisher.emit('jkl','我是王五')
    //输出
    //张三 js发布订阅模式
    //李四 js发布订阅模式
    //王五 我是王五
    //赵六 我是王五
</script>

实现2

<script>

    /*
    * 向消息队列里面添加内容
    * 移除消息队列里面的内容
    * 触发消息队列里面的内容
    * */
    class Observer{
        constructor() {
            this.message={}
        }
        //type要传入的数据类型
        //fn行为发生之后要做的事情
        on(type,fn){
            //先判断有没有这个属性
            if(!this.message[type]){
                //如果没有这个属性,就初始化一个空数组
                this.message[type]=[]
            }
            //如果没有这个属性,就往它的后面push一个新的fn
            this.message[type].push(fn)
        }
        //取消订阅
        off(type,fn){
            //1,先判断有没有订阅
            if(!this.message[type]) return
            //2,判断有没有fn这个参数
            if(!fn){
                //如果没有fn就删除掉整个事件
                this.message[type]=undefined
                return
            }
            //如果有fn就只是仅仅过滤掉这个方法
            this.message[type] = this.message[type].filter((item)=>item!==fn)
        }
        //进行
        emit(type){
            //判断有没有订阅
            if(!this.message[type]) return
            this.message[type].forEach((item)=>{
                item()//执行这个方法
            })
        }
    }
    //使用构造函数创建一个实例
    const Person1 = new Observer();
    //我现在想要向person1委托一些内容,帮我进行观察

    //例子1
    console.log(Person1)
    Person1.on('fun',fun1)
    Person1.on('fun',fun2)
    Person1.on('fun',fun3)
    Person1.off('fun',fun2)//取消事件2
    //进行触发
    Person1.emit('fun')
    function fun1(){
        console.log("我是事件1")
    }
    function fun2(){
        console.log("我是事件2")
    }
    function fun3(){
        console.log("我是事件3")
    }
    //输出
    //我是事件1
    //我是事件3
    //例子2
    Person1.on('js',f1)
    Person1.on('js',f2)
    Person1.on('js',f3)
    Person1.emit('js')
    function f1(){
        console.log("张三")
    }
    function f2(){
        console.log("李四")
    }
    function f3(){
        console.log("王五")
    }
    //输出张三,李四,王五

</script>

3,优点和缺点

优点

1,对象之间可以解耦

2,异步编程中,可以更松耦合的代码编写

缺点

1,创建订阅者本身要消耗一定的时间和内存

2,能弱化对象之间的联系,多个发布者和订阅者嵌套一起的时候,程序难以维护