前端设计模式之发布订阅

147 阅读2分钟

事件发布订阅模式简单实现:

实现思路:
  1. 声明一个类 EventEmitter
  2. 添加私有属性this.db对象,用来保存事件
  3. emit() 方法,用来发布事件
  4. on() 方法,用来订阅事件
  5. once() 方法,用来订阅事件并且只执行一次事件回调,然后就销毁
  6. off() 方法,用来注销某种类型事件的某个回调(一种类型的事件一般都有多处订阅,所以有多个回调)
具体代码实现:
export class EventEmitter {
  constructor() {
    // 添加私有属性this.db对象,用来保存事件,键名为事件类型,键值为事件回调数组
    this.db = Object.create(null)
  }
  // 发布事件
  emit(type, payload) {
    // 做一次缓存并将对象转换为数组
    const handlers = (this.db[type] || []).slice()
    // 发布事件的同时也要触发订阅了这个事件的回调们
    if (handlers && handlers.length) {
      handlers.forEach(handler => handler(payload))
    }
  }
  // 订阅事件
  on(type, handler) {
    // 如果这种类型的事件是第一次订阅,这基于这个类型的事件创建一个数组,保存这个类型事件的回调们
    if (!this.db[type]) {
      this.db[type] = []
    }
    // 如果已存在这种类型的事件,则直接往这个事件的回调数组添加回调
    this.db[type].push(handler)
    return this
  }
  // 只订阅一次,意味着这个回调只会执行一次
  once(type, handler) {
    const emiter = this
    // 实现方法很简单,其实就是重写了这个回调,让这个回调执行完毕之后就调用off方法销毁
    function on() {
      emiter.off(type, on)
      handler.apply(emiter, arguments)
    }
    emiter.on(type, on)
  }
  // 注销某种类型事件的某个回调
  off(type, handler) {
    const handlers = this.db[type]
    // 如果此种类型的事件存在回调
    if (handlers) {
      // 遍历回调数组
      for (let i = 0; i < handlers.length; ++i) {
	// 找到那个要销毁的回调,把它从事件的回调数组中删除
        if (handlers[i] === handler) {
          handlers.splice(i, 1)
          break
        }
      }
    }
  }
}
// 最后导出一个EvnentEmitter的实例对象,保证使用的都是同一个实例对象
export const eventEmitter = new EventEmitter()