EventEmitter

class EventEmitter {
  handlers
  constructor() {
    this.handlers = new Map()
  }


  on(eventName, cb) {
    if (!this.handlers.has(eventName)) {
      this.handlers.set(eventName, [])
    }

    this.handlers.get(eventName).push(cb)
  }

  emit(eventName, ...args) {
    const callbacks = this.handlers.get(eventName)
    if (callbacks) {
      // 这里需要对 this.handlers[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
      const handlers = callbacks.slice()
      handlers.forEach((callback) => {
        callback(...args)
      })
    }
  }


  off(eventName, cb) {
    const callbacks = this.handlers.get(eventName)
    if (callbacks) {
      const index = callbacks.indexOf(cb)
      if (index !== -1) {
        callbacks.splice(index, 1)
      }
    }
  }

  once(eventName, cb) {
    // 对回调函数进行包装,使其执行完毕自动被移除
    const wrapper = (...args) => {
      cb(...args)
      this.off(eventName, wrapper)
    }
    this.on(eventName, wrapper)
  }
}
class EventEmitter {
  handlers: Map<string, ((...args: unknown[]) => void)[]>
  constructor() {
    this.handlers = new Map<string, ((...args: unknown[]) => void)[]>()
  }

  on(eventName: string, cb: (...args: unknown[]) => void) {
    if (!this.handlers.has(eventName)) {
      this.handlers.set(eventName, [])
    }

    this.handlers.get(eventName)!.push(cb)
  }

  emit(eventName: string, ...args: unknown[]) {
    const callbacks = this.handlers.get(eventName)
    if (callbacks) {
      // 这里需要对 this.handlers[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
      const handlers = callbacks.slice()
      handlers.forEach((callback) => {
        callback(...args)
      })
    }
  }

  off(eventName: string, cb: (...args: unknown[]) => void) {
    const callbacks = this.handlers.get(eventName)
    if (callbacks) {
      const index = callbacks.indexOf(cb)
      if (index !== -1) {
        callbacks.splice(index, 1)
      }
    }
  }

  once(eventName: string, cb: (...args: unknown[]) => void) {
    // 对回调函数进行包装,使其执行完毕自动被移除
    const wrapper = (...args: unknown[]) => {
      cb(...args)
      this.off(eventName, wrapper)
    }
    this.on(eventName, wrapper)
  }
}

在线演示:

上面包含了一个EventEmitter常用的方法,下面的开源实现提供了更多功能:

  • emitteropen in new window:出自facebook,用JavaScript实现的,代码量不多
  • mittopen in new window:用TypeScript写的,0依赖,源码总共才100多行,去掉类型、注释和空行,真正的源码逻辑才几十行,很适合阅读
Last Updated: