实现一个EventBus

137 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情

你好,我是南一。这是我在准备面试八股文的笔记,如果有发现错误或者可完善的地方,还请指正,万分感谢🌹

一、简介

EventBus又叫事件总线,是发布-订阅设计模式的一种实现,用于线程或组件间的通信。今天要讲的是EventBus的实现,以及在React中组件通信的应用。

二、ES6实现

EventBus基础的四个方法,on订阅,off取消订阅,emit发布,once只订阅一次。

为了全局通用,对EventBus做了单例导出。

class EventBus {
  static instance = null
  static events = {}
  constructor() {
    if (!EventBus.instance) {
      EventBus.instance = this
    }
    return EventBus.instance
  }

  on(type, callback) {
    if (EventBus.events[type]) {
      EventBus.events[type].push(callback)
    } else {
      EventBus.events[type] = [callback]
    }
  }

  off(type, callback) {
    if (!EventBus.events[type]) return console.log('不存在该事件');
    if (!EventBus.events[type].includes(callback)) return console.log('该事件不存在此回调函数');
    const index = EventBus.events[type].indexOf(callback);
    EventBus.events[type].splice(index, 1)
  }

  emit(type, ...payload) {
    if (!EventBus.events[type]) return console.log('不存在该事件');
    EventBus.events[type].forEach(callback => callback(...payload))
  }

  once(type, callback) {
    let cb = (...payload) => {
      callback(...payload)
      this.off(type, cb)
    }
    this.on(type, cb)
  }
}

三、EventTarget实现

浏览器提供的EventTarget接口,其本身就带有注册事件,删除事件监听,派发的方法。再利用CustomEvent创建一个自定义的事件

class EventBus {
  constructor() {
    this.bus = document.createElement('fakeelement');
  }

  addEventListener = (eventType, callback) => {
    this.bus.addEventListener(eventType, callback)
  }

  removeEventListener = (eventType, callback) => {
    this.bus.removeEventListener(eventType, callback)
  }

  dispatchEvent = (eventType, detail = {}) => {
    this.bus.dispatchEvent(new CustomEvent(eventType, { detail }));
  }
}

四、观察者模式

观察者模式与发布订阅模式比较相似,两者的不同之处在于,观察者模式中被观察者与观察者知道对方的存在。发布订阅模式中,发布者与订阅者通过消息通道进行通信,无需知道对方的存在,解耦更彻底。

以下为观察者模式的代码实现,观察者可以主动申请加入观察队列,也可以被被观察者加入观察队列

// 被观察者
class Subject {
  constructor() {
    this.observerList = []
  }

  addObserver(observer) {
    this.observerList.push(observer)
  }

  removeObserver(observer) {
    const index = this.observerList.findIndex(o => o.name === observer.name);
    this.observerList.splice(index, 1);
  }

  notifyObservers(message) {
    const observers = this.observerList;
    observers.forEach(observer => observer.notified(message));
  }
}

// 观察者
class Observer {
  constructor(name, subject) {
    this.name = name;
    subject && subject.addObserver(this);
  }

  notified(message) {
    console.log(this.name, 'got message', message);
  }
}

let subject = new Subject()
let observerA = new Observer('observerA', subject)
let observerB = new Observer('observerB')
subject.addObserver(observerB)
subject.notifyObservers('Hello')