#36 实现一个 EventEmitter


  • 0

    如果对没有注册的事件进行触发或删除,则会报错。因此,多做一层 isOn 判断更好。

    class EventEmitter {
    	constructor() {
    		this.events = {};
    	}
    
    	on(eventName, func) {
    		let isOn = eventName in this.events;
    		let fns = isOn ? this.events[eventName] : [];
    		fns.push(func);
    		this.events[eventName] = fns;
    	}
    
    	emit(eventName, ...args) {
    		let isOn = eventName in this.events;
    		if (!isOn) return console.log(`Error: ${eventName} 事件未注册, 无法触发`);
    
    		this.events[eventName].forEach(fn => fn.call(this, ...args))
    	}
    
    	off(eventName, func) {
    		let isOn = eventName in this.events;
    		if (!isOn) return console.log(`Error: ${eventName} 事件未注册,无法删除`);
    
    		let fns = this.events[eventName];
    		let i = fns.findIndex(fn => fn == func);
    		fns.splice(i, 1)
    	}
    }
    

  • 0

    class EventEmitter {
        constructor() {
            this.events = {};
        }
        
        on(eventName, func) {
          this.events[eventName] = this.events[eventName] || []
          this.events[eventName].push(func)
        }
        
        emit(eventName, ...args) {
          this.events[eventName].forEach(func => {
      	func.call(null, ...args)
          })
        }
        
        off(eventName, func) {
          const e = this.events[eventName]
          e.splice(e.indexOf(func), 1)
        }
    }
    

    选对数据结构是关键啊..


  • 0

    简单实现是这样的,虽然通过了测试,但是真正的 EventEmitter 应该要考虑一些特殊情况吧:

    class EventEmitter {
      constructor() {
        this.listeners = []
      }  
      
      on(event, func) {
        this.listeners.push({
          event,
          func
        })
      }
      
      emit(event, ...args) {
        this.listeners.forEach((listener) => {
          if(listener.event === event) listener.func(...args)
        })
      }
      
      off(event, func) {
        this.listeners.forEach((listener, index) => {
          if(listener.event === event && listener.func === func) this.listeners.splice(index, 1)
        })
      }
    }
    

  • 0

    @PARADISELIN this.messageBox[type].push(handle)时是不是应该检查handle的类型,不然emit中调用时会出错?


  • 0

    class EventEmitter {
      /* TODO */
      constructor(){
        this.store = []; 
        this.fns = {};
      }
      on(eventName, func){
        this.fns[eventName] = func;
        this.store.push(this.fns);
        this.fns = {};
      }
      emit(eventName, ...props){
        this.store.forEach( (fns) => {
          if(fns[eventName]) fns[eventName](...props);
        })
      }
      off(eventName, func){
        this.store.forEach( (fns, index) => {
          if(fns[eventName] === func){
            this.store.splice(index, 1);
            return false;
          }
        })
      }
    }
    

  • 0

    class EventEmitter {
        /* TODO */
        constructor() {
    
            this.events = {};
        }
    
        on(type, fun) {
            if(!this.events.hasOwnProperty(type)){
                 this.events[type] = [];
            }
            this.events[type].push(fun);
        }
        off(type,fun){
            let funs = this.events[type];
            if(!funs || funs.length < 1){
                return false;
            }
            if(!fun){
                return delete this.events[type];
            }
            if(funs.includes(fun)){
                const index = funs.indexOf(fun);
                funs.splice(index,1);
            }
            if(funs.length < 0){
                return delete this.events[type];
            }
            this.events[type] = funs;
    
        }
        emit(){
            let type = arguments[0],arg = [];
            for(let i =1;i<arguments.length;i++){
                arg.push(arguments[i]);
            }
            let funs = this.events[type];
            if(!funs || funs.length < 1){
                return false;
            }
            funs.forEach(fun => {
                fun.apply(this,arg);
            });
        }
    }
    

  • 0

    @jason94
    Array.prototype.shift.call(arguments),这个用的好,我还是自己循环取了一遍


  • 0

    我参照大家给出的比较好的代码
    
    class EventEmitter {
      constructor(){
        this.events={}
      }
      /* TODO */
      on(e,f) {
        const callback = this.events[e]||[]
        callback.push(f)
        this.events[e] = callback
      }
      emit(e,...params) {
        if(this.events.hasOwnProperty(e)) {
          const callbacks = this.events[e]
          callbacks.map((f)=>{
            f(...params)
          })
        }
      }
      off(e,f) {
            if(this.events.hasOwnProperty(e)) {
          const callbacks = this.events[e]
          if(callbacks.includes(f)) {
            let index = callbacks.indexOf(f)
            callbacks.splice(index,1)
          }
        }
      }
    }
    

  • 0

    为啥感觉虽然写对了却稀里糊涂的就对了。。。。。

    class EventEmitter {
      constructor (){
        this.listenerobj = {};
      }
    
      on (funcname, listener){
        if(!this.listenerobj[funcname]){
          this.listenerobj[funcname] = [];
        }
        this.listenerobj[funcname].push(listener);
      }
      emit (funcname, ...args){
        this.listenerobj[funcname].forEach((item, idx) => {
          item(...args);
        });
      }
      off (funcname, targetlistener){
        this.listenerobj[funcname].splice(this.listenerobj[funcname].indexOf(targetlistener),1);
      }
    }
    
    

  • 0

    感觉这个练习把题目做出来并不难,主要是为了通过这道题熟悉观察者模式的套路和实现方式。怎样完美地解耦等等。

    class EventEmitter {
      constructor (){
        this.listenerobj = {};
      }
    
      on (funcname, listener){
        this.listenerobj[funcname] = this.listenerobj[funcname] || [];
        this.listenerobj[funcname].push(listener);
      }
      emit (funcname, ...args){
        this.listenerobj[funcname].forEach((item, idx) => {
          item(...args);
        });
      }
      off (funcname, targetlistener){
        this.listenerobj[funcname].splice(this.listenerobj[funcname].indexOf(targetlistener),1);
      }
    }
    
    

  • 0

    @ScriptOJ 新手想请教一下这个为啥不通过,提示'off 的时候只删除一次',但是我测试时,事件名及函数相同时,都可以删掉。

    class EventEmitter {
      constructor() {
        this.eventArr = [];
      }
      on(eventName,fun){
        this.eventArr.push({'eventName':eventName,'fun':fun});
      }
      emit(eventName,...args){
        this.eventArr.forEach((obj)=>{
          if(obj.eventName==eventName)
          {
            var argumentsArr = Array.prototype.slice.call(...args);
            obj.fun(...args);
          }
        })
      }
      off(eventName, func){
       var self = this;
       var arr=[]
        this.eventArr.forEach((obj,index)=>{
          if (obj.eventName != eventName || obj.fun!=func)
          {
            arr.push(obj);
          }
        })
        this.eventArr = arr;
      }
      /* TODO */
    }
    

  • 0

    class EventEmitter{
          constructor(){
            this.events = new Map()
            this.funcs = new Set()
          }
          on(eventName,func){
            this.events.set(eventName,this.funcs.add(func))
          }
          emit(eventName,...args){
            for(let [key,val] of this.events.entries()){
              if(eventName === key){
                for(let i of this.funcs.values()){
                  i(...args)
                }
              }
            }
          }
          off(eventName,func){
            for(let [key,val] of this.events.entries()){
              if(eventName === key && val.has(func)){
                this.funcs.delete(func)
              }else{
                this.evetns.delete(key)
              }
            }
          }
        }
    

    示例全部通过,提交提示事件没有被触发


  • 0

    class EventEmitter {
      /* TODO */
      constructor() {
        this.handlers = {}
      }
      on(eventName, handle) {
        if(!this.handlers.hasOwnProperty(eventName)){
          this.handlers[eventName] = []
        }
        this.handlers[eventName].push(handle)
      }
      off(eventName, handle) {
        if(!this.handlers.hasOwnProperty(eventName)) return 
        //获取下标,并删除
        let index = this.handlers[eventName].indexOf(handle)
        this.handlers[eventName].splice(index,1)
      }
      emit(eventName, ...params) {
        if(!this.handlers.hasOwnProperty(eventName)) return 
        //事件队列依次执行
        this.handlers[eventName].map(handle => {
          handle(...params)
        })
      }
    }
    
    

  • 0

    class EventEmitter {
    /* TODO */
    constructor() {
    this.listeners = [];
    }
    on(eventName,func){
    if(this.listeners.filter((listener)=>
    listener.eventName === eventName && listener.func == func
    ).length==0){
    this.listeners.push({
    eventName:eventName,
    func:func
    });
    }
    }
    emit(eventName,...arg){
    const dealListeners = this.listeners.filter((listener)=>{
    return listener.eventName === eventName
    });
    for (let i in dealListeners){
    dealListeners[i].func(...arg);
    }
    }
    off(eventName, func){
    for (let i in this.listeners){
    if(this.listeners[i].eventName === eventName && this.listeners[i].func == func){
    this.listeners.splice(i,1);
    break;
    }
    }
    }
    }
    我在浏览器跑可以 为什么提示事件没触发


  • 0

    终于搞对了,半小时
    class EventEmitter{

    		constructor(){
    			this.events= {};
    		}
    
    		on(eventName,fn){
    			let fnList = this.events[eventName]||[];
    			fnList.push(fn)
    			if(eventName){
    				this.events[eventName] = fnList;
    			}
    		}
    
    		emit(eventName,...agr){
    			let funcs = this.events[eventName];
    			if(funcs && funcs.length){
        			for(let j=0;j<funcs.length;j++){
        				funcs[j](...agr);
        			}
        		}
        	}
    		
    		off(eventName,fn){
    			let funcs = this.events[eventName];
    			if(fn){
    				  this.events[eventName].splice(fn,1);
    			}else{
    				delete this.events[eventName]
    			}
    		}
    	}

  • 0

    class EventEmitter {
      constructor () {
        this.store = {}
      }
    
      on (type, method) {
        if (this.store.hasOwnProperty(type)) {
          this.store[type].push(method)
        } else {
          this.store[type] = []
          this.store[type].push(method)
        }
      }
    
      emit (type, ...args) {
        for (let m of this.store[type]) {
          m(...args)
        }
      }
    
      off (type, method) {
        let index = this.store[type].indexOf(method)
        this.store[type].splice(index, 1)
      }
    }
    

    怎么感觉还有些东西没考虑到...


  • 0

    class EventEmitter {
      constructor() {
        this.onList = [];
      }
      on(name, fn) {
        this.onList.push({
          name,
          fn
        });
      }
      emit(name, ...param) {
        this.onList.map((o) => {
          if (o.name === name) {
            o.fn(...param);
          }
        })
      }
      off(name, fn) {
        let i = this.onList.findIndex((value) => {
          return value.name == name;
        })
        if (~i && fn == this.onList[i].fn) {
          this.onList.splice(i, 1);
        }
      }
    }
    

    自己测试貌似是可以,但是提交的时候总是提醒 :参数没有传入


  • 0

    class EventEmitter {
      constructor() {
        this.events = {};
      }
    
      on(eventName, callback) {
        if (!this.events[eventName]) {
          this.events[eventName] = []
        }
        this.events[eventName].push(callback);
        return this;
      }
    
      emit(eventName, ...args) {
        if (this.events[eventName].length) {
          this.events[eventName].forEach(fn => {
            if (typeof fn === 'function') {
              fn(...args);
            }
          });
        }
        return this;
      }
    
      off(eventName, callback) {
        if (this.events[eventName].length) {
          const pos = this.events[eventName].findIndex(fn => fn === callback);
          this.events[eventName].splice(pos, 1);
        } else {
          delete this.events[eventName];
        }
        return this;
      }
    }
    

    可以链式调用


  • 0

    很简洁的写法

    class EventEmitter {
      constructor() {
        this.events = new Map()
      }
      
      on(event, cb) {
        this.events.has(event) ? this.events.get(event).push(cb) : this.events.set(event, [cb])
      }
      
      emit(event, ...args){
        this.events.get(event).map(v => v(...args))
      }
      
      off(event, cb) {
        this.events.get(event).forEach((v, i) => {
          if(cb === v) this.events.get(event).splice(i,1)
        })
      }
    }
    

  • 0

    @Saku#36 实现一个 EventEmitter 中说:

    对设计模式一脸懵逼 参考代码后写了出来 理解后还是挺好懂的

    class EventEmitter {
      constructor() {
        this.handlers = {}
      }
      
      on(eventName, func) {
        let callbacks = eventName in this.handlers ? this.handlers[eventName] : []
        callbacks.push(func)
        this.handlers[eventName] = callbacks
      }
      
      emit(eventName, ...args) {
        if (!eventName in this.handlers) return
        const callbacks = this.handlers[eventName]
        callbacks.map(cb => {
          cb(...args)
        })
      }
      
      off(eventName, func) {
        if (!eventName in this.handlers) return
        let callbacks = this.handlers[eventName]
        let index = callbacks.indexOf(func)
        callbacks.splice(index, 1)
      }
    }
    

登录后回复
 

与 ScriptOJ 的连接断开,我们正在尝试重连,请耐心等待