手写发布订阅模式(手写系列二)

764 阅读1分钟

背景

以下是手写系列内容预告,初步计划一周一个主题。

手写观察者/发布订阅模式

Subject是构造函数,new Subject() 创建一个主题对象,该对象内部维护订阅当前主题的观察者数组。主题对象上有一些方法,如添加观察者(addObserver)、删除观察者(removeObserver)、通知观察者更新(notify)。 当notify 时实际上调用全部观察者 observer 自身的 update 方法。

Observer 是构造函数,new Observer() 创建一个观察者对象,该对象有一个 update 方法,观察者可以订阅主题,实际上是把自己加入到主题的订阅者列表里。

class Subject {
  observers = []

  addObserver(observer) {
    this.observers.push(observer)
  }
  removeObserver(observer) {
    let index = this.observers.indexOf(observer)
    if(index > -1){
      this.observers.splice(index, 1)
    }
  }
  notify() {
    this.observers.forEach(observer => {
      observer.update()
    })
  }
}

class Observer{
  update() {}
  subscribeTo(subject) {
    subject.addObserver(this)
  }
} 

测试代码

let subject = new Subject()
let observer = new Observer()
observer.update = function() {
  console.log('observer update')
}
observer.subscribeTo(subject)  //观察者订阅主题

subject.notify()

手写一个EventBus

观察者模式和发布订阅模式具体的写法和区别没有官方的定义,下面这种写法反而更常见。

class EventBus {
  map = {} 

  on(type, handler) {
    this.map[type] = (this.map[type] || []).concat(handler)
  }

  fire(type, data) {
    this.map[type] && this.map[type].forEach(handler => handler(data))
  }

  off(type, handler) {
    if(this.map[type]) {
      if(!handler) {
        delete this.map[type]
      } else {
        let index = this.map[type].indexOf(handler)
        this.map[type].splice(index, 1)
      }
    }
  }
}

测试代码

const eventBus = new EventBus()

eventBus.on('click:btn', data => {
  console.log(data)
})

eventBus.fire('click:btn', {a: 1, b: 2})
eventBus.off('click:btn')
eventBus.fire('click:btn', {a: 1, b: 2})

如果觉得有用,点个赞让作者开心一下呗~

补充