手写Event.EventEmitter

手写Event.EventEmitter

一个EventEmitter需要实现以下方法:

  • addEventListener
  • removeEventlistener
  • removeAllEventListener
  • emit
  • once

EventEmitter

function EventEmitter(){
    this.events = new Map()
}
//工具函数
const wrapCallBack = (fn,once = false) => ({ callback:fn,once })

addEventListener

EventEmitter.prototype.addEventListener = function(type,fn,once = false){
    //从events获取事件类型为type的事件回调
    let handler = this.events.get(type);
    // console.log(handler)
    if(!handler){
        //如果不存在 则添加
        this.events.set(type,wrapCallBack(fn,once))
    }else if(handler && typeof handler.callback === 'function'){
        //如果只绑定一个回调
        this.events.set(type,[handler,wrapCallBack(fn,once)])
    }else {
        //绑定类多个回调
        this.events.set(type,[...handler,wrapCallBack(fn,once)])
    }
}

removeEventListener

EventEmitter.prototype.removeListener = function(type,listener){
    let handler = this.events.get(type);
    if(!handler) return;
    if(!Array.isArray(handler)){
      //简单比较回调函数是否相同
        if(String(handler.callback) === String(listener)){
            this.events.delete(type)
        }else {
            return
        }
    }else{
      //循环回调函数队列
        for (let i = 0; i < handler.length; i++) {
            const item = handler[i];
            //简单比较回调函数是否相同
            if(String(item.callback) === String(listener)){
                handler.splice(i,1);
                //避免回调函数数组变化 导致数组访问错误
                i--;
            }
        }
        //如果数组为空 删除监听的事件即可
        if(handler.length === 0){
            this.events.delete(type)
        }else if(handler.length === 1){
              //如果数组只有一个元素 只需要以对象的形式保存即可
            this.events.set(type, handler[0]);
        }
    }
}

removeAllEventListener

EventEmitter.prototype.removeAllListener = function(type){
    let handler = this.events.get(type);
    if(!handler) return 
    this.events.delete(type)
}

emit

EventEmitter.prototype.emit = function(type,...args){
    let handler = this.events.get(type),
        eventsArray = [];
      //如果是回调队列为数组的形式 循环遍历
    if(Array.isArray(handler)){
        //简单实现 避免后续数组长度出现变化 对数组的访问出错
        //优化:可以考虑写一个工具函数 深拷贝
        for (let i = 0; i < handler.length; i++) {
            eventsArray.push(handler[i])
        }
          //遍历type对应的回调队列 触发每一个cb
        for (let i = 0; i < eventsArray.length; i++) {
            const item = eventsArray[i];
            //执行回调
            item.callback.apply(this,args);
            if(item.once){
                  //如果回调函数只执行一次 则删除该回调函数
                this.removeListener(type,item.callback)
            }
        }
    }else {
          //否则直接执行即可
        handler.callback.apply(this,args)
    }
    return true
}

once

EventEmitter.prototype.once = function(type,fn){
    this.addEventListener(type,fn,true)
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!