手写发布订阅

167 阅读1分钟

发布订阅

定义

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

实现

const eventhub = {
  map:{},  
  on:(event,fn)=>{
    eventhub.map[event] = eventhub.map[event] || []  //防御式编程,考虑参数为空的情况
    eventhub.map[event].push(fn)
    return undefined
  },
  off:(event,fn)=>{
    const q = eventhub.map[event]  //alias 别名、缩写,一般用于读操作,不用于写操作,否则可能会出现引用错误
    if(!q) {return}
    const index = q.indexOf(fn)
    if(index < 0){return}  // 短路
    q.splice(index,1)
    return undefined
  },
  emit:(event,data)=>{
    const q = eventhub.map[event]
    if(!q) return
    q.map(fn =>{
      fn.call(undefined,data)
    })
    return undefined
  },
}
eventhub.on('click',console.log)
eventhub.on('click',console.error)
eventhub.off('click',console.log)

setTimeout(()=>{
  eventhub.emit('click','jason')
})

用 class 实现

class EventHub{
  map = {}
  on(event,fn){
    this.map[event] = this.map[event] || []
    this.map[event].push(fn)
  }
  off(event,fn){
    if(!this.map[event]) return
    const index = this.map[event].indexOf(fn)
    if(index < 0) return
    this.map[event].splice(index,1)
  }
  emit(event,data){
    const fnList = this.map[event] || []
    fnList.forEach(fn => fn(data))
  }
  //先注册,事件执行后取消订阅
  once(event,fn){
  //需要在回调函数执行后,取消订阅当前事件,所以需要对传入的回调函数做一层包装,然后绑定包装后的函数
    const onceFn = (...args)=>{
      fn.call(undefined,...args)
      this.off(event,onceFn)
    }
    this.on(event,onceFn)
  }
}

const e = new EventHub()
e.on('click', (name)=>{
  console.log('hi '+ name)
})
e.on('click', (name)=>{
  console.log('hello '+ name)
})
setTimeout(()=>{
  e.emit('click', 'jason')
},3000)