观察者模式(发布订阅模式)的简单实现,以及实际应用举例

615 阅读2分钟

观察者模式实现

鉴于最近在归纳总结一些常用的设计模式,刚好最近小程序项目中跨组件的异步通信中仿造vue总线机制实现了一个用于订阅和发布消息的对象。所以将js中观察者模式实现总结如下。

简介

  1. 目的:实现发布订阅的功能,取消订阅功能的observer对象
  2. 是一种解决封装良好的组件之间异步通信的一种方式
  3. 典型应用是vue中的总线机制

主要包含的内容

  1. 发布函数,发布的时候可能会携带参数
  2. 订阅函数,收到消息之后执行回调函数
  3. 一个缓存订阅者以及订阅者的回调函数的列表
  4. 取消订阅函数。(需要考虑情况较多)

代码实现如下

function Observer() {
    this.cache = {}  //用于存储订阅的事件名称以及回调函数列表的键值对
}

Observer.prototype.on = function (key,fn) {//key:订阅消息的类型的标识(名称),fn收到消息之后执行的回调函数
    if(!this.cache[key]){
        this.cache[key]=[]
    }
    this.cache[key].push(fn)
}

Observer.prototype.emit = function (key) { //arguments 是发布消息时候携带的参数数组
    if(this.cache[key]&&this.cache[key].length>0){
        var fns = this.cache[key]
    }
    for(let i=0;i<fns.length;i++){
        Array.prototype.shift.call(arguments)
        fns[i].apply(this,arguments)
    }
}
// remove 的时候比较有意思,如果你直接传入一个匿名函数fn,那么你在remove的时候是无法找到这个函数并且把它移除的,变通方式是传入一个
//指向该函数的指针,而 订阅的时候存入的也是这个指针
Observer.prototype.remove = function (key,fn) {
    let fns = this.cache[key]
    if(!fns||fns.length===0){
        return
    }
    //如果没有传入fn,那么就是取消所有该事件的订阅
    if(!fn){
        fns=[]
    }else {
        fns.forEach((item,index)=>{
            if(item===fn){
                fns.splice(index,1)
            }
        })
    }
}


//example


var obj = new Observer()
obj.on('hello',function (a,b) {
    console.log(a,b)
})
obj.emit('hello',1,2)
//取消订阅事件的回调必须是具名函数
obj.on('test',fn1 =function () {
    console.log('fn1')
})
obj.on('test',fn2 = function () {
    console.log('fn2')
})
obj.remove('test',fn1)
obj.emit('test')

实际开发中的使用方式

  1. vue中在vue对象上就实现了该发布订阅功能。传送门
  2. 仿造vue中的总线机制在wepy的小程序项目中定制一个用于组件之间异步通信的总线。
//app.wpy中挂载在wepy对象上:
wepy.$bus = new Observer();
//不同组件(或者页面)之中的分别调用
wepy.$bus.on('test2',function(){
    console.log('this is test 2')
})
wepy.$bus.emit('test2')