serendipityApe / javascriptPromotion

资深前端必备的编码问题
3 stars 0 forks source link

简单实现EventEmitter #18

Open serendipityApe opened 2 years ago

serendipityApe commented 2 years ago

题目

create an Event Emitter

例子

Node.js中有Event Emitter,Facebook 也曾经有自己的实现 不过已经archive了。

请实现你自己的 Event Emitter

const emitter = new Emitter() 它需要支持事件订阅

const sub1  = emitter.subscribe('event1', callback1)
const sub2 = emitter.subscribe('event2', callback2)

// 同一个callback可以重复订阅同一个事件
const sub3 = emitter.subscribe('event1', callback1)

emit(eventName, ...args) 可以用来触发callback

emitter.emit('event1', 1, 2);
// callback1 会被调用两次

subscribe()返回一个含有release()的对象,可以用来取消订阅。

sub1.release()
sub3.release()
// 现在即使'event1'被触发, 
// callback1 也不会被调用

我的解答

class EventEmitter {
  constructor() {
    //维护一个订阅该对象的map
    //map:  (eventName,[callback...]),key为eventName,value是由相同eventName的回调函数组成的数组
    this.watcher = new Map();
  }
  subscribe(eventName, callback) {
    var watcher=this.watcher;  
    if (!watcher.has(eventName)) {
      //eventName不存在,则添加
      watcher.set(eventName, [callback]);
    } else {
      //存在则向对应value里增加callback
      watcher.set(eventName, [...watcher.get(eventName),callback]);
    }
    return {
      //返回一个包含release方法的对象
      release: function () {
        //找到对应的callback,删除
        //此时利用闭包,使用的是第10行的watcher
        watcher.get(eventName).map((item,index) => {
          if (item == callback) {
            watcher.get(eventName).splice(index,1);
          } else {
            return item;
          }
        })
      }
    }
  }

  emit(eventName, ...args) {
    if (this.watcher.has(eventName)) {
      //eventName存在则依此调用watcher里的callback
      this.watcher.get(eventName).forEach(call => {
        call.apply(this, args);
      })
    }
  }
}