手写一个基于发布订阅模式的js事件处理中心

135 阅读1分钟

发布订阅模式:

关键内容:off,on,emit,once四个方法

代码实现:

class EventEmitter {
  constructor() {
    this._events = {};
  }

  //注册事件
  on(eventName, cb) {
    const cbs = this._events[eventName] || []; //若本身注册过,则取之前的值,否则置为空数组
    cbs.push(cb);
    this._events[eventName] = cbs;
  }
  //发布订阅,并调度执行
  emit(eventName, ...args) {
    const cbs = this._events[eventName] || [];
    //将调度中心同名注册下的所有事件都执行一遍
    cbs.forEach((cb) => cb(...args));
  }

  off(eventName, cb) {
    const cbs = this._events[eventName] || [];
    //第二个并级判断,是为了配合once的特殊注册
    this._events[eventName] = cbs.filter(
      (filtItem) => filtItem !== cb && filtItem.cb !== cb
    );
  }
//思想:在执行一次过后就将对应函数off掉
  once(eventName, cb) {
    const one = (...args) => {
      cb(...args);
      this.off(eventName, one);
    };
    //此处非常重要,若不添加,则在once注册之后,emit之前就off则无法删除对应注册回调
    one.cb = cb;
    this.on(eventName, one);
  }
}

const events = new EventEmitter();
function handleName(name) {
  console.log("name1", name);
}
function handleName2(name) {
  console.log("name2", name);
}

events.on("hello", handleName);
events.once("hello", handleName2);
//此时once注册的时候存储的回调函数跟实际传递的回调函数是不相等的,在没有emit之前就直接off,是没有办法删除的
events.off("hello", handleName2);

events.emit("hello", "小明");
events.emit("hello", "小王");

注意:

若one.cb=cb这句话不添加,则结果是:

name1:小明;name2:小明(没有off掉);name1:小王