JavaScript发布-订阅模式与开发实践(下)

88 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

前言

作为一个前端切图仔,少有和各类设计模式打交道。但这不影响我们学习设计模式的思维,来提升我们的代码水平。

本章通过讲解发布—订阅模式,希望能够让你对设计模式更一步的学习。

本章学习内容👇

  • 完成一个完整的发布—订阅模式
  • 总结发布—订阅模式

完成一个完整的发布—订阅模式

在上一章中,我们实现了一个简易的发布—订阅模式。

参考:《JavaScript发布-订阅模式与开发实践(中)》

在这一章节中,我们着手于完善一个完整的**发布—订阅模式**。

以下根据功能,我们逐步实现。首先👇

  • 实现订阅到具体属性值

我们修改缓存池的结构,并在发布者实例化时赋予属性。在依赖注入时,通过键名缓存回调函数👇

class Publisher{
      constructor(obj) {
        this.obj = obj
        this.cache = {}
        }
      dependent(key,callback){
        if (!this.cache[key]){ //如果该属性还没有被订阅
            this.cache[key] = []
        }
        this.cache[key].push(callback)
    }
}

接着👇

  • 实现,通知订阅者时,只通知与属性改变相关的订阅者

触发器通知订阅者时,检查具体哪个属性发生改变,只调用该属性缓存池中的回调函数。

class Publisher{
    constructor(obj) {
        this.obj = obj
        this.cache = {}
    }
​
    dependent(key,callback){
        if (!this.cache[key]){ //如果该属性还没有被订阅
            this.cache[key] = []
        }
        this.cache[key].push(callback)
    }
​
    trigger(key){
        if(this.cache[key]){
            this.cache[key].forEach((callback)=>{
                callback(key,this.obj[key])
            })
        }
    }
}

我们通过一段测试代码来测试下这个功能👇

let publisher = new Publisher({ age:15, money:1 })
​
let subscriber1 = {
    name:'one',
}
​
let subscriber2 = {
    name:'two'
}
​
publisher.dependent('age',(key,value)=>{console.log(`${subscriber1.name} listen key:${key}, value change to ${value}`)})
publisher.dependent('money',(key,value)=>{console.log(`${subscriber2.name} listen key:${key}, value change to ${value}`)})
​
publisher.trigger('age')
publisher.trigger('money')

控制台输出

one listen key:age, value change to 15
two listen key:money, value change to 1

看来是完成了这个功能🤩

但是还存在一个问题,看看以下这段代码。

  window.addEventListener('resize', ()=>{ console.log('size change')});
  window.removeEventListener('resize', ()=>{ console.log('size change')});

我们在能够订阅属性的同时,同样也要赋予能够取消订阅属性的能力

因此👇

  • 实现,订阅者能够取消订阅的功能
class Publisher{
    constructor(obj) {
        this.obj = obj
        this.cache = {}
    }
​
    dependent(key,callback){
        if (!this.cache[key]){ //如果该属性还没有被订阅
            this.cache[key] = []
        }
        this.cache[key].push(callback)
    }
​
    trigger(key){
        if(this.cache[key]){
            this.cache[key].forEach((callback)=>{
                callback(key,this.obj[key])
            })
        }
    }
​
    unsubscribe( key , fn){
        if (!this.cache[key]){
            return false
        }
        if (!fn){
            this.cache[key] = []
            console.log(`已清除属性值:${key}的所有通知`)
        }else{
            this.cache[key].forEach((callback,index)=>{
                if ( callback.toString() === fn.toString()){
                    this.cache[key].splice(index,1)
                    console.log(`已清除属性值:${key}的一个通知`)
                }
            })
​
        }
​
​
    }
}

同样的,我们通过一段测试代码来测试下这个功能👇

let publisher = new Publisher({ age:15, money:1 })
​
let subscriber1 = {
    name:'one',
}
publisher.dependent('age',(key,value)=>{console.log(`${subscriber1.name} listen key:${key}, value change to ${value}`)})
let subscriber2 = {
    name:'two'
}
publisher.dependent('age',(key,value)=>{console.log(`${subscriber2.name} listen key:${key}, value change to ${value}`)})
​
publisher.trigger('age')
​
publisher.unsubscribe('age',(key,value)=>{console.log(`${subscriber1.name} listen key:${key}, value change to ${value}`)})
​
publisher.unsubscribe('age')

控制台输出

one listen key:age, value change to 15
two listen key:age, value change to 15
已清除属性值:age的一个通知
已清除属性值:age的所有通知

这样我们就实现了一个完整的**发布—订阅模式**了🎉🎉🎉

总结

在这个章节中,我们逐步的学习了**发布—订阅模式**,并完成了从零的开发实践。

发布—订阅模式的优点相比大家都清楚了,那么缺点呢?

试想,我们放在回调池里的函数是不是会一直存在于我们的内存当中?即使,它从未触发。

并且,我们实现注入依赖这一步骤时也会消耗一定的时间和内存。

再者,如果过度使用/不正确依赖关系的话。可能会导致我们程序之间的逻辑难以维护。

因此,任何看起来便利的模式。我们在使用时,都应该三思而后行。

最后,阅读到这里的看官老爷,能不能赏口饭吃?三连可否?😏

\