#36 实现一个 EventEmitter


  • 0

    从react小书那边跳过来...刚入门完全不知道神马设计模式...只知道模块模式...硬着头皮写了半个多小时的一堆挫代码

    希望哈老师能帮忙批改一下 感谢

    class EventEmitter {
      constructor() {
        this.messageBox = {};
      }
    
      on(type, handle) {
        if (!this.messageBox.hasOwnProperty(type)) {
          this.messageBox[type] = [];
          handle && this.messageBox[type].push(handle);
        } else {
          handle && this.messageBox[type].push(handle);
        }
      }
    
      emit(type, ...args) {
        if (this.messageBox.hasOwnProperty(type)) {
          const handleQueen = this.messageBox[type];
          handleQueen.forEach((handle, index, arr) => {
            handle(...args);
          })
        }
      }
    
      off(type, handle) {
        if (this.messageBox.hasOwnProperty(type)) {
          const handleQueen = this.messageBox[type];
          if (handleQueen.includes(handle)) {
            const atIndex = handleQueen.indexOf(handle);
            handleQueen.splice(atIndex, 1)
          }
        }
      }
    }
    

  • 2
    administrators

    @PARADISELIN 写得很不错啊,有些地方可能可以简化一下,例如说 hasOwnProperty 来判断是没有必要的,on 里面的判断可以简化一下:

    const callbacks =  this.messageBox[type] || []
    callbacks.push(handle)
    this.messageBox[type] = callbacks
    

    这样会不会好一些?

    有些地方名字可以改一下 handleQueen 这个应该是笔误?


  • 0

    @胡子大哈 对哦 应该是handleQueue。。。谢谢哈哥 我去改一下


  • 0

    class EventEmitter {
      constructor(){
        this.fns={}
        this.cnt={}
      }
      on(str,fn){
        this.fns[str]=this.fns[str]||[]
        this.cnt[str]=this.cnt[str]||0
       fn.index=this.cnt[str]++
        this.fns[str].push(fn)
      }
      emit(str,...args){
        this.fns[str].forEach(function(fn){
          fn(...args)
        })
      }
      off(str,fn){
        this.fns[str].splice(fn.index,1)
      }
    }
    

    这么写为什么不行啊
    忘了不同事件类型的情况了,现在通过了,但是没考虑匿名函数,如果是匿名函数怎么取消事件订阅,各位大神帮忙看看


  • 2

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

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

  • 0

    class EventEmitter {
    
        constructor() {
            this.messageBox = {};
        }
    
        on(eventName, func) {
            const callbacks = this.messageBox[eventName] || [];
            callbacks.push(func);
            this.messageBox[eventName] = callbacks;
        }
    
        emit(eventName, ...args) {
            const callbacks = this.messageBox[eventName];
            if (callbacks.length > 0) {
                callbacks.forEach((callback) => {
                    callback(...args);
                });
            }
        }
    
        off(eventName, func) {
            const callbacks = this.messageBox[eventName];
            const index = callbacks.indexOf(func);
            if (index !== -1) {
                callbacks.splice(index, 1);
            }
        }
    }
    

  • 0

    class EventEmitter{
        constructor(){
            this.events = {}
        }
    
        on(eventName,func){
            if(!this.events.hasOwnProperty(eventName)){
                this.events[eventName] = []
            }
    
            this.events[eventName].push(func)
        }
    
        emit(eventName,...rest){
            if(!this.events.hasOwnProperty(eventName)){
                return false;
            }
    
            this.events[eventName].forEach(event=>{
                event.call(this,...rest)
            })
        }
    
        off(eventName, func){
            if(!this.events.hasOwnProperty(eventName)){
                return false;
            }
    
            this.events[eventName] = this.events[eventName].filter(event=> event!==func)
        }
    }
    

  • 1

    请看看这样写如何

    class EventEmitter {
      constructor(){
        this.topics = {};
      }
      on(topic, func){
        if(!this.topics[topic]){
          this.topics[topic] = []
        }
        this.topics[topic].push({
          token: topic,
          func: func
        })
      }
      emit(...args){
        let topic = arguments[0]
        if(!this.topics[topic]){
          return false
        }
        var s = this.topics[topic],
        len = s ? s.length : 0
        while(len--){
          s[len].func(...args.slice(1))
        }
        return true
      }
      off(topic, func){
        var len = this.topics[topic].length
        for(var i=0; i<len; i++){
          if(this.topics[topic][i].func === func){
            this.topics[topic].splice(i, 1)
            return true
          }
        }
        return false
      }
    }
    
    

  • 0

    class EventEmitter {
      constructor(){
        this.list = [];
      }
      on(name, func){
        this.list.push({"name" : name, "func" : func});
      }
      emit(name, ...params){
        for(let i in this.list){
          if(this.list[i].name == name){
            this.list[i].func(...params)
          }
        }
      }
      off(name, func){
        for(let i in this.list){
          if(func === this.list[i].func){
            this.list.splice(i, 1);
          }
        }
      }
    }
    

  • 0

    class EventEmitter {
        /* TODO */
        constructor () {
            this.eventQueue = {} 
        }
      
        on(eventName, func) {
            if (!this.eventQueue[eventName]) {
                this.eventQueue[eventName] = [];
            }
            this.eventQueue[eventName].push(func); 
        }
    
        emit(eventName) {
            if (!this.eventQueue[eventName]) return;
            let args = Array.from(arguments);
            args.shift();
            this.eventQueue[eventName].forEach((func) => {
                if ( typeof func === 'function' ) {
                    func.apply(null, args);
                }
            })
        }
    
        off(eventName, func) {
            if (this.eventQueue[eventName]) {
                this.eventQueue[eventName].forEach(function(element, index) {
                    if (typeof emlement !== 'function' || typeof element === 'function' && element === func ) {
                        delete this.eventQueue[eventName][index]
                        return
                    }
                })
            }
        }
    }
    

    console 测试通过,题目页面提交提示有错,请指出问题,谢谢。


  • 0
    administrators

    @pengcc off 函数里面的那个 this.eventQueue[eventName][index] 和外层的 this 不是同一个 this


  • 0

    @ScriptOJ 确实,问题是在console没出错啊?


  • 0
    administrators

    @pengcc 可能是你自己的测试还没有触发到运行到那一行的情况,你多试几种情况就会报错了。


  • 0

    @ScriptOJ 我用的题目页面的测试,刚才把off中,let that = this, 提示了新的错误,如果用同一个函数监听同一个事件多次,off 的时候只删除一次,我再细想一下,多谢了。this是个明显的错误,我忽略了。


  • 0

    class EventEmitter {
        /* TODO */
        constructor () {
            this.eventQueue = {} 
        }
      
        on(eventName, func) {
            if (!this.eventQueue[eventName]) {
                this.eventQueue[eventName] = [];
            }
            this.eventQueue[eventName].push(func); 
        }
    
        emit(eventName) {
            if (!this.eventQueue[eventName]) return;
            let args = Array.from(arguments);
            args.shift();
            this.eventQueue[eventName].forEach((func) => {
                if ( typeof func === 'function' ) {
                    func.apply(null, args);
                }
            })
        }
    
        off(eventName, func) {
            if (this.eventQueue[eventName]) {
                for ( let index in this.eventQueue[eventName] ) {
                    let item = this.eventQueue[eventName][index]
                    if (typeof item !== 'function' || (typeof item === 'function' && item === func) ) {
                        delete this.eventQueue[eventName][index]
                    }
                }
            }
        }
    }
    

    题目页面提交还是出错,错误信息:”Wrong Answer,
    如果用同一个函数监听同一个事件多次,off 的时候只删除一次“,console中测试没有问题,同一个函数监听同一个事件多次,off时明明都删除了,在题目页面上一直提示只删除一次,求指出错误。


  • 0

    @pengcc 已解决,理解错了off的要求。


  • 0

    核心思路是酱紫的,创建一个列表缓存回调函数;当发布者发布时,遍历缓存列表,依次触发回调函数;

    class EventEmitter {
        constructor() {
            this.eventList = {};
        }
        on(key, fn) {
            if (!this.eventList[key]) {
                this.eventList[key] = [];
            }
            this.eventList[key].push(fn);
        }
        emit() {
            let key = Array.prototype.shift.call(arguments),
                fns = this.eventList[key];
            fns.forEach(fn => {
                fn.apply(null, arguments);
            })
        }
        off(key, fn) {
            let fns = this.eventList[key];
            if (!fns) {
                return false;
            } else {
                fns.forEach((_fn,index)=>{
                    if(_fn === fn){
                        fns.splice(index,1);
                    }
                })
            }
        }
    }
    

  • 0

    本地正常,测试未通过。

    class EventEmitter {
      constructor() {
        this.events = new Map()
      }
    
      on(eventName, func) {
        if (this.events.has(eventName)) {
          const listeners = this.events.get(eventName)
          this.events.set(eventName, [...new Set([...listeners, func])])
        } else {
          this.events.set(eventName, [func])
        }
      }
    
      off(eventName, func) {
        if (this.events.has(eventName)) {
          const listeners = this.events.get(eventName)
          this.events.set(eventName, listeners.filter(l => l !== func))
        }
      }
    
      emit(eventName, ...args) {
        if (this.events.has(eventName)) {
          const listeners = this.events.get(eventName)
          listeners.forEach(l => l(...args))
        }
      }
    }
    

  • 0

    class EventEmitter {
        constructor() {
          this.event = {}
        }
        on(name, fn) {
          const fnArr = this.event[name] || []
          fn && fnArr.push(fn)
          this.event[name] = fnArr
        }
        emit(name, ...arg) {
          if (this.event.hasOwnProperty(name)) {
            for (const fn of this.event[name]) {
              fn(...arg)
            }
          }
        }
        off(name, fn) {
          if (this.event.hasOwnProperty(name)) {
            const fnArr = this.event[name] || []
            const findIndex = fnArr.indexOf(fn)
            fnArr.splice(findIndex, 1)
            this.event[name] = fnArr
          }
        }
      }

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

登录后回复
 

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