设计模式:发布-订阅模式的 JS 实现

介绍

发布-订阅是一种消息范式,通过定义发布者订阅者调度中心来完成消息通信。这是前端常用的消息通讯模式。

事件由调度中心统一管理,由订阅者调度中心订阅(注册)事件,发布者通过调度中心发布(触发)事件,订阅者通过事件收到消息,以达到消息通信

优点

  • 松耦合:由于发布者和订阅者并不直接通信,而是通过和调度中心交互,达到消息广播和接收的目的。对象之间无需关心对方。

缺点

  1. 创建订阅者需要消耗一定的时间和内存。

  2. 虽然可以弱化对象之间的联系,如果过度使用的话,反而使代码不好理解及代码不好维护等等。

JS 实现

  1. eventBus.map: 存储事件队列的 map, 每个事件都有一个单独的队列,存放所有的事件处理函数
  2. eventBus.on(eventName, handler): 订阅事件的方法,根据传入的eventName事件名,将handler追加到新建或存在的事件队列中
  3. eventBus.emit(eventName, args): 触发事件的方法,根据传入事件名称、参数遍历事件队列并触发事件
  4. eventBus.off(eventName, handler): 取消事件订阅,根据事件名和处理函数取消事件订阅,如不传入处理函数,则清空相应的事件队列
  5. eventBus.once(eventName, handler): 执行单次事件订阅,触发后自动清除订阅
const eventBus = {
  // map: 存储事件队列的 map, 每个事件都有一个单独的队列,存放所有的事件处理函数
  map: new Map(),
  // on: 订阅事件的方法,根据传入的 eventName 事件名,将handler追加到新建或存在的事件队列中
  on(eventName, handler) {
    const handlers = this.map.get(eventName)
    if (handlers) {
      handlers.push(handler)
    } else {
      this.map.set(eventName, [handler])
    }
  },
  // emit: 触发事件的方法,根据传入事件名称、参数遍历事件队列并触发事件
  emit(eventName, args) {
    const handlers = this.map.get(eventName)
    console.log(handlers)
    if (!handlers) {
      throw new Error(`${eventName} is not exist`)
    }
    handlers.forEach((handler) => {
      handler(args)
    })
  },
  // off: 取消事件订阅,根据事件名和处理函数取消事件订阅,如不传入处理函数,则清空相应的事件队列
  off(eventName, handler) {
    if (!handler) {
      this.map.set(eventName, [])
      return
    }
    const handlers = this.map.get(eventName)
    const index = handlers.indexOf(handler)
    if (index >= 0) {
      handlers.splice(index, 1)
    }
  },
  // once: 执行单次事件订阅,触发后自动清除订阅
  once(eventName, handler) {
    const tempHandler = (args) => {
      this.off(eventName, tempHandler)
      handler(args)
    }
    this.on(eventName, tempHandler)
  },
}
复制代码

可以使用Map自带的clear清空EventBus

eventBus.map.clear()

其他

DOMEevnt 事件方法也是一个典型的发布-订阅模式。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享