nodejs之EventEmitter实现

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。

Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

以下简单实现:

function EventEmitter() {
  this.events = {}
  this.counts = 0
  this.maxNum = 10
  // 内置事件:newListener -> 事件在添加新监听器时被触发。removeListener -> 事件在接除监听器时被触发。
  this.innerEvent = {
    NEWLISTENER: 'newListener',
    REMOVELISTENER: 'removeListener'
  }
}

// 为指定事件添加一个监听器到监听器数组的尾部。
function addListener(eventName, callback) {
  if (typeof callback !== 'function') return

  if (!this.events[eventName]) {
    if (!this._isInnerEvent(eventName)) {
      this.events[eventName] = [{ type: 'on', callback }]
      this.counts++
      if (this.counts > this.maxNum) {
        console.warn(`目前监听器数量:${this.counts}个,监听器已经超过${this.maxNum}个`)
      }
      if (this.events[this.innerEvent.NEWLISTENER]) {
        this.emit(this.innerEvent.NEWLISTENER, eventName)
      }
    } else {
      this.events[eventName] = { type: 'on', callback }
    }
  } else {
    this.events[eventName].push({ type: 'on', callback })
  }
}

EventEmitter.prototype = {
  _toString: function (obj) {
    return Object.prototype.toString.call(obj).slice(8, -1)
  },

  _isInnerEvent: function (eventName) {
    return !!this.innerEvent[eventName.toUpperCase()]
  },

  addListener: addListener,
  on: addListener,

  // 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
  emit: function () {
    let arg = Array.prototype.slice.call(arguments)
    let eventName = arg[0]
    let params = arg.slice(1)

    if (!this._isInnerEvent(eventName)) {
      if (this._toString(this.events[eventName]) === 'Array' && this.events[eventName].length) {
        this.events[eventName].forEach(event => {
          let { type, callback } = event
          callback.apply(null, params)
          if (type === 'once') {
            this.events[evtName].splice(index, 1)
          }
        })
        return true
      }
      return false
    } else {
      this.events[eventName].callback.apply(null, params)
      if (this.events[eventName].type === 'once') {
        delete this.events[eventName]
      }
    }
  },

  // 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
  once: function (eventName, callback) {
    if (typeof callback !== 'function') return

    if (!this.events[eventName]) {
      if (!this._isInnerEvent(eventName)) {
        this.events[eventName] = [{ type: 'once', callback }]
        this.counts++
        if (this.counts > this.maxNum) {
          console.warn(`目前监听器数量:${this.counts}个,监听器已经超过${this.maxNum}个`)
        }
        if (this.events[this.innerEvent.NEWLISTENER]) {
          this.emit(this.innerEvent.NEWLISTENER, eventName)
        }
      } else {
        this.events[eventName] = { type: 'once', callback }
      }
    } else {
      this.events[eventName].push({ type: 'once', callback })
    }
  },

  // 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。
  removeListener: function (eventName, callback) {
    if (this._toString(this.events[eventName]) === 'Array') {
      let _events = this.events[eventName].concat()

      for (let i = 0; i < _events.length; i++) {
        if (_events[i].callback === callback) {
          this.events[eventName].splice(i, 1)
          return
        }
      }
    }
  },

  // 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
  removeAllListeners: function (eventName) {
    if (eventName) {
      if (this.events[eventName]) {
        delete this.events[eventName]
        if (!this._isInnerEvent(eventName)) {
          this.counts--
          if (this.events[this.innerEvent.REMOVELISTENER]) {
            this.emit(this.innerEvent.REMOVELISTENER, eventName)
          }
        }
      }
    } else {
      this.events = {}
      this.counts = 0
    }
  },

  // 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
  setMaxListeners: function (num) {
    this.maxNum = num
  },

  // 返回指定事件的监听器数组。
  listeners: function (eventName) {
    if (this._toString(this.events[eventName]) === 'Array') {
      let _events = this.events[eventName].concat()
      let newArray = []
      _events.forEach(item => {
        newArray.push(item.callback)
      })
      return newArray
    }
  },

  // 返回指定事件的监听器数量
  listenerCount: function (eventName) {
    if (this._toString(this.events[eventName]) === 'Array') {
      return this.events[eventName].length
    }
  }
}

var eventEmit = new EventEmitter()

eventEmit.on('newListener', function newListener(eventName) {
  console.log('>>>>newListener ---', eventName)
})
eventEmit.on('removeListener', function removeListener(eventName) {
  console.log('>>>>removeListener ---', eventName)
})

console.log(eventEmit)

function event1() {
  console.log('event1')
}
eventEmit.on('event', event1)

eventEmit.on('event1', event1)

eventEmit.on('event', function event2() {
  console.log('event2')
})

eventEmit.emit('event')
eventEmit.emit('event1')