前端基础知识之手写发布订阅

928 阅读2分钟

今天来总结一下如何手写一个简单的发布订阅模式。简单来说,发布订阅包含三大功能,包括监听事件、触发事件和取消监听事件。我们可以用两种方式来实现,分别是普通的对象和类实现:

普通实现

const eventHub = {
    map: {},
    on: (name, fn)=>{
    //入队
    //if(eventHub.map[name] === undefined){
    //	eventHub.map[name] = []
    //}
    eventHub.map[name] = eventHub.map[name] || []
    eventHub.map[name].push(fn)
    },
    emit:(name, data)=>{
	const q = eventHub.map[name]
        if(!q){return}
	q.map(f => f.call(undefined,data))
        return undefined
    }, //trigger
    off:(name, fn)=>{
        const p = eventHub.map[name]; //alias 设计模式
	if (!p){return}
        const index = p.indexOf(fn)
	if(index<0){return}
        p.splice(index, 1)
    }
}

eventHub.on('click',f1) //监听事件,把f1放到任务队列中(先进先出)

eventHub.off('click',f1)  //取消监听事件

setTimeout(()=>{
	eventHub.emit('click','frank') //触发事件
},3000)

由于我们需要避免在多次调用发布订阅事件时顺序错乱的问题,所以我们构造了一个 map 作为任务队列。

其中,监听事件方法 on 我们需要判断是否任务队列中已经有相同的时间,如果没有就置为空数组,有的话就保留,然后 push 到 map 中。

而触发事件方法 emit 时,我们也需要判断是否任务队列中有相对应的事件,如果没有,则直接返回;如果有,则通过 map 方法找到每一个函数,并一一执行,最后 return undefined(因为一般不关心返回值)。

对于取消监听事件方法 off,同样的如果任务队列中并没有相对应的事件,那就直接返回;反之则定义一个 index 作为函数 fn 的下标,如果函数不存在,即下标小于零,则直接返回;反之则通过数组的 splice 方法去除相对应下标的函数。

类实现

除了普通的实现方法,也可以使用 JS 的类方法来实现发布订阅:

class EventHub {
  map = {}
  on(name, fn) {
    this.map[name] = this.map[name] || []
    this.map[name].push(fn)
  }
  emit(name, data) {
    const fnList = this.map[name] || []
    fnList.forEach(fn => fn.call(undefined, data))
  }
  off(name, fn) {
    const fnList = this.map[name] || []
    const index = fnList.indexOf(fn)
    if(index < 0) return
    fnList.splice(index, 1)
  }
}
// 使用
const e = new EventHub()
e.on('click', (name)=>{
  console.log('hi '+ name)
})
e.on('click', (name)=>{
  console.log('hello '+ name)
})
setTimeout(()=>{
  e.emit('click', 'frank')
},3000)

整体思路是一样的,但是在方法的实现上会有类和函数的一些区别。

©本总结教程版权归作者所有,转载需注明出处