Skip to content

Latest commit

 

History

History
83 lines (74 loc) · 3.09 KB

设计模式-发布订阅.md

File metadata and controls

83 lines (74 loc) · 3.09 KB

发布-订阅模式广泛应用于异步编程中,这是一种替代传递回调函数的方案。比如,我们可订阅 ajax 请求的 success error等事件, 发布订阅可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的接口。发布订阅模式让两个对象松耦合的联系在一起,虽然不清楚彼此的实现细节,但这并不影响他们之间的相互通信。

从现实的例子开始

小明想买房,到了售楼处被告知,该楼盘的房子早已售罄。售楼MM告诉小明,不久后还有一些尾盘推出,时间还未定。 小明记下了售楼处的电话,以后每天都会打电话询问是否可购买。除了小明,还有小红、小强也会每天向售楼处咨询这个问题。可能售楼处每天回答 1000个相同内容的电话。 当然现实中销售公司会一一将客户的电话记录下来,新楼盘推出的时候,售楼 MM会翻开花名册,遍历上面的电话号码,依次发送一条短信来通知他们。

简单实现

class Event {
  constructor () {}
  // 首先定义一个事件容器,用来装事件数组(因为订阅者可以是多个)
  handlers = {}

  // 事件添加方法,参数有事件名和事件方法
  addEventListener (type, handler) {
    // 首先判断handlers内有没有type事件容器,没有则创建一个新数组容器
    if (!(type in this.handlers)) {
      this.handlers[type] = []
    }
    // 将事件存入
    this.handlers[type].push(handler)
  }

  // 触发事件两个参数(事件名,参数)
  dispatchEvent (type, ...params) {
    // 若没有注册该事件则抛出错误
    if (!(type in this.handlers)) {
      return new Error('未注册该事件')
    }
    // 便利触发
    this.handlers[type].forEach(handler => {
      handler(...params)
    })
  }

  // 事件移除参数(事件名,删除的事件,若无第二个参数则删除该事件的订阅和发布)
  removeEventListener (type, handler) {
      // 无效事件抛出
      if (!(type in this.handlers)) {
        return new Error('无效事件')
      }
      if (!handler) {
        // 直接移除事件
        delete this.handlers[type]
      } else {
        const idx = this.handlers[type].findIndex(ele => ele === handler)
        // 抛出异常事件
        if (idx === undefined) {
          return new Error('无该绑定事件')
        }
        // 移除事件
        this.handlers[type].splice(idx, 1)
        if (this.handlers[type].length === 0) {
          delete this.handlers[type]
        }
      }
    }
}


//使用这个单例模式
let event = new Event() // 创建event实例
// 定义一个自定义事件:"load"
function load (params) {
  console.log('load', params)
}
event.addEventListener('load', load)
// 再定义一个load事件
function load2 (params) {
  console.log('load2', params)
}
event.addEventListener('load', load2)
// 触发该事件
event.dispatchEvent('load', 'load事件触发')
// 移除load2事件
event.removeEventListener('load', load2)
// 移除所有load事件
event.removeEventListener('load')