介绍

发布订阅模式是一种多对一的关系。即订阅者订阅不同的主题,当发布者发布相关的主题的时候呢,订阅者可以收到相关的发布的信息。

实现方式

看代码中的注释,发布订阅就是根据主题来做,并且存进相应的订阅回调。
通俗的说呢,就是list保存订阅的时候的回调函数,发布的时候传参数进去,被订阅每个回调函数都去执行,那么这个传进去的参数就是我们发布的时候携带的内容啦啦啦。

let events = {
  list: {}, //存订阅的回调函数
  // 订阅事件
  on(key, fn) {
    !this.list[key] && (this.list[key] = []);
    this.list[key].push(fn);
  },
  // 发布事件
  emit() {
    let key = [].shift.call(arguments);
    let fns = this.list[key];
    if (!fns || fns.length === 0) {
      return false;
    }
    fns.forEach((fn) => {
      fn.apply(this, arguments);
    });
  },
  // 取消订阅
  remove(key, fn) {
    let fns = this.list[key];
    if (!fns) return false;
    // 如果没有传入对应的函数,将key值对应的缓存函数都清空掉
    if (!fn) {
      fns && (fns.length = 0);
    } else {
      fns.forEach((cb, i) => {
        cb === fn && fns.splice(i, 1);
      });
    }
  },
  // 订阅一次
  once(key, fn) {
    // 订阅的回调函数
    function callback() {
      // 先取消前面一次的订阅,再重新订阅
      this.remove(key, callback);
      fn.apply(this, arguments);
    }
    // 把主题key包装成callbakc的属性,这样的目的是因为取消订阅的时候取消的是订阅的回调函数,也就是callback。
    // 这就是为啥不用this.remove(key,fn)
    // 此时传到remove的整个callback函数(包括fn属性的)。因为this.on(key,callback)才调用callback函数(这里会比较难理解)
    callback.fn = fn;
    this.on(key, callback);
  },
};

function cat(data) {
  console.log("一起喵喵喵");
  console.log(data); // ["xxx","yyy"]
}
function dog() {
  console.log("一起汪汪汪");
}

// 就是去订阅pet主题的事件,
events.on("pet", (data) => {
  console.log("接受数据");
  console.log(data);
});

events.on("pet", cat);
events.on("pet", dog);
// 取消dog的订阅 没有pet主题的话,就把对应的函数全部清空掉(相当于取消了该主题所有的订阅)
events.remove("pet", dog);
// 发布这些事件之后呢,订阅者就可以接受到新发布的东西啦,这些新的东西呢其实就是["xxx","yyy"]
events.emit("pet", ["xxx", "yyy"]);

console.log(events.list);
/*
接受数据
[ 'xxx', 'yyy' ]
一起喵喵喵
{ pet: [ [Function (anonymous)], [Function: cat] ] }
*/
  1. 创建一个存放订阅者的对象
  2. on方法用来把回调函数 fn 都加到缓存列表中
  3. emit方法取到arguments里第一个当做key,根据key值去执行对应缓存列表中的函数
  4. remove方法可以根据key值取消订阅