#36 实现一个 EventEmitter


  • 0

    提交显示第二次事件没有触发,不知道哪里错了,求帮助

    class EventEmitter {
      /* TODO */
      constructor(){this.data={}}
      on(e,func){
        if(!this.data[e]){
          this.data[e]=[]
        }
          this.data[e].push(func)
      }
      emit(e,...arg){
        if(!this.data[e])return
        let len=this.data[e].length
        let i=0
        for(i=0;i<len;i++){
          this.data[e][i].call(this,...arg)
        }
      }
      off(e){
        if(!this.data[e])return
        let len=this.data[e].length-1
        for(let i=len;i>0;i--){
          this.data[e].pop()
        }
      }
    }
    

  • 0

    http://jsbin.com/yebelul/1/edit?html,console
    这是我的代码,在jsbin和chrome调试都能成功,但是在提交答案就不行了,胡子大大给看看呗


  • 0

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

  • 0

    写完了,这个题去面试的话当场写出来的一定漏洞百出,本地好像输出正常的,提交报elem.func is not a function,求点评代码中的不足:)
    本地输出:

    Hello ScriptOJ
    Good night, ScriptOJ
    Good night, ScriptOJ
    I am Jerry, and I am 12 years old
    

    代码:

    class EventEmitter {
    	constructor() {
    		this.methodList = []; //监听队列,结构:[{eventName: 事件名,func: 方法},...]
    	}
    	loopList(eventName, func) { //遍历监听队列
    		let arr = this.methodList,
    			temp = [],
    			result = arr.map(function(elem, index) {
    				if (func === undefined && elem.eventName === eventName) {
    					return elem; //触发事件时,接收一个参数,返回对应事件的监听队列
    				} else if (elem.eventName === eventName && elem.func === func)
    					return index; //停止监听时,传入两个参数,返回对象在监听队列中的下标
    				else
    					return -1;
    			});
    		return result;
    	}
    	handleInput(eventName, func) { //输入验证
    		if (eventName && func)
    			return true;
    		else {
    			return false;
    			console.log('wrong input!');
    		}
    	}
    	on(eventName, func) { //监听方法,将事件和方法添加到监听队列
    		if (this.handleInput(eventName, func))
    			this.methodList.push({
    				eventName: eventName,
    				func: func
    			});
    	}
    	emit(eventName, temp) { //事件触发方法,根据eventName参数遍历方法队列,
    		let args = Array.from(arguments); //返回相应的方法;展开arguments队列,传入方法并调用。
    		args.splice(0, 1);
    		if (eventName) {
    			let arr = this.loopList(eventName);
    			arr.map(function(elem) {
    				return elem.func(...args);
    			})
    		} else {
    			console.log('wrong input!');
    		}
    	}
    	off(eventName, func) { //停止监听,删除监听队列中相应的值
    		if (this.handleInput(eventName, func)) {
    			let index = this.loopList(eventName, func);
    			index !== -1 ? this.methodList.splice(index, 1) : console.log('wrong eventName or func ');
    		}
    	}
    }
    

  • 0

    function EventEmitter() {
    const eventList = []
    const on = (name, func) => eventList.push([name, func])
    const emit = (name, ...rest) => {
    eventList.forEach(item => {
    if (item[0] === name) item1
    })
    }
    const off = (name, func) => eventList.splice(eventList.findIndex(val => val[0] === name && val[1] === func), 1)
    return { on, emit, off }
    }
    做构造器使用添加至this就好


  • 0

    class EventEmitter {
    constructor() {
    this.listens = {}
    }

    on(event, func) {
    if (!this.listens[event]) this.listens[event] = []
    this.listens[event].push(func)
    }

    emit(event, ...args) {
    if (this.listens[event]) {
    this.listens[event].forEach(func => func.apply(null, args))
    }
    }

    off(event, func) {
    if (func.name) {
    this.listens[event] = this.listens[event].filter(f => func !== f)
    } else {
    this.listens[event] = []
    }
    }
    }

    我这么写有什么问题呢,我在浏览器跑正常的。


  • 0

    class EventEmitter {
      constructor(){
        this.sub = {}
      }
      on(eventName,cb){
        if(!this.sub[eventName]){
          this.sub[eventName] = []
        }
        this.sub[eventName].push(cb)
      }
      emit(eventName,...args){
        this.sub[eventName].map(cb => {
          cb.apply(this,args)
        })
      }
      off(eventName,cb){
        if(this.sub[eventName].includes(cb)){
          let index = this.sub[eventName].indexOf(cb)
          this.sub[eventName].splice(index,1)
        }
      }
    }
    
    

  • 0

    时间没触发我devtools试了都没有问题呀

        class EventEmitter {
          constructor(){
            this.id = Math.random();
          }
          on(eventName, func){
            if(!EventEmitter[eventName]){
              EventEmitter[eventName] = {};
            }
            EventEmitter[eventName][this.id] = func;
          }
          off(eventName, func){
            delete EventEmitter[eventName][this.id] 
          }
          emit(eventName){
            let funcArr =  Object.keys(EventEmitter[eventName])
            let params = Array.prototype.slice.call(arguments);
            params.splice(0,1)
            funcArr.forEach((vaule)=>{
                EventEmitter[eventName][vaule](...params)
            })
          }
        }
    

  • 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)
              }
            }
          }
        }
    

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


登录后回复
 

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