面试官:实现一个事件订阅和发布的事件中心

177 阅读1分钟

tiny-emitter

功能,一个事件订阅和发布的事件中心

思考

一个事件中心应该包含哪些功能

· 发布

· 订阅

· 取消订阅

· 一次执行

基本上起码要包含以上几种方法

code

// 第一步:新建一个类别
function E () {

}
// 原型
E.prototype = {
  // on 方法
  // e是一个对象,维护了事件的名称和cb
  // e: {
      'eventName': [{
          ctx: _this1,
          fn: () => {console.log(1)}
      }, {
          ctx: _this2,
          fn: () => {console.log(3)}
      }]
  }
  on: function (name, callback, ctx) {
    var e = this.e || (this.e = {});

    (e[name] || (e[name] = [])).push({
      fn: callback,
      ctx: ctx
    });

    return this;
  },
  
  // once 方法
  // 创建一个listener,通过调用off,在执行一次就移除掉
  once: function (name, callback, ctx) {
    var self = this;
    function listener () {
      self.off(name, listener);
      callback.apply(ctx, arguments);
    };

    listener._ = callback
    return this.on(name, listener, ctx);
  },

  // emit方法

  emit: function (name) {
    // emit 参数
    var data = [].slice.call(arguments, 1);
    
    // 当前事件下面的所有cb的拷贝
    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
    var i = 0;
    var len = evtArr.length;
    
    // 执行
    for (i; i < len; i++) {
      evtArr[i].fn.apply(evtArr[i].ctx, data);
    }

    return this;
  },

  // off 方法

  off: function (name, callback) {
    var e = this.e || (this.e = {});
    // 某个事件 cb list
    var evts = e[name];
    var liveEvents = [];
    // 如果存在 evts and cb
    // 过滤出cb不一致的
    // 这里可以用filter改造

    // const liveEvents = evts.filter(item => item.fn != callback && item.fn._ != callback /* 用于once的取消订阅 */)
    if (evts && callback) {
      for (var i = 0, len = evts.length; i < len; i++) {
        if (evts[i].fn !== callback && evts[i].fn._ !== callback)
          liveEvents.push(evts[i]);
      }
    }

    // 将过滤掉的重新赋值
    // 或则直接删除掉eventName
    (liveEvents.length)
      ? e[name] = liveEvents
      : delete e[name];

    return this;
  }
};

module.exports = E;
module.exports.TinyEmitter = E;