公布订阅

定义了对象间的一种一对多的依赖关系,当一个对象的状态产生扭转时,所依赖它的对象都将失去告诉

举个例子

你网购一个洗面奶,然而那家店目前断货了,于是你每天都到那家店询问货到否,和你一样的购物者(假如有1000)个,每天都问雷同的问题,这样是不好了,商家想了一个方法,留下你们必要的信息,货到时一一告诉你们。那么咱们就能够把商家当作发布者,购物者就是订阅者

作用:

  1. 购物者不必每天都向商家询问,商家也不必每天都回复,只须要在适当的工夫,告诉购物者货到就行
  2. 购物者和商家不毕强耦合,购物者的减少只须要留下必要的信息,商家不须要理解购物者的其余状况。商家的任何变动也不会影响购物者

实现

let shop = {  // 发布者    listenList: [],    addListen(cb) {    // 增加订阅者        this.listenList.push(cb);    },    trigger:function(){ // 公布音讯        for(let i = 0; i < this.listenList.length; i++) {            this.listenList[i].apply(this, arguments);   // argument是公布音讯时带上的参数        }    }}shop.addListen(function(msg) {          // 购买者1订阅音讯    console.log('购买者1承受到', msg);})shop.addListen(function(msg) {          // // 购买者2订阅音讯    console.log('购买者2承受到', msg);})shop.trigger('洗面奶到货的音讯');// 这里咱们能够理解到 购买者(订阅者) 的减少,不会影响到 商家(发布者)// 商家(发布者)新增属性或办法(如xxx)也不会影响到 购买者(订阅者),也就是松耦合

// 下面实现的只是最简略的公布订阅模式,还存在一些问题。// 假如这家商店不止卖洗面奶,而且还卖防晒霜,护肤品等等,有些购物者只想接管洗面奶的信息,而有些只想接管防晒霜的信息,下面这种写法会把洗面奶,防晒霜等其余信息都推送给用户,这对用户体验是不好的,所以咱们须要一个key,只推送用户喜爱的商品,代码实现如下let shop = {  // 发布者    listenList: {},    addListen(key, cb) {    // 增加订阅者        if(!this.listenList[key]) {    // 没订阅过这个音讯,则创立一个缓存列表            this.listenList[key] = [];        }        this.listenList[key].push(cb);    },    trigger:function(){ // 公布音讯        // 获取要发送音讯的类型        let key = Array.prototype.shift.call(arguments),            fns = this.listenList[key],            len = fns.length;        // 发送音讯        for(let i = 0; i < len; i++) {            fns[i].apply(this, arguments);        }    }}shop.addListen('cleansingMilk', function(msg) {          // 购买者1订阅cleansingMilk音讯    console.log('购买者1承受到', msg);})shop.addListen('cleansingMilk', function(msg) {          // // 购买者2订阅cleansingMilk音讯    console.log('购买者2承受到', msg);})shop.addListen('sunblock', function(msg) {          // 购买者3订阅sunblock音讯    console.log('购买者3承受到', msg);})shop.addListen('sunblock', function(msg) {          // 购买者4订阅sunblock音讯    console.log('购买者4承受到', msg);})shop.trigger('cleansingMilk', '洗面奶到货的音讯');   // 这样只获取了洗面奶的音讯shop.trigger('sunblock', '防晒霜到货的音讯');      // // 这样只获取了防晒霜的音讯// 下面的代码还短少一些严谨性,然而为了可能更容易了解,我摈弃了一些细节性的货色
// 一个商店不止卖护肤品,还有玩具,身物,咱们能够封装一个类(这里咱们增加了勾销订阅的事件)class Event {    constructor() {        this.listenList = {};    }    addListen(key, cb) {        if(!this.listenList[key]) {             this.listenList[key] = [];        }        this.listenList[key].push(cb);    }    trigger() {        let key = Array.prototype.shift.call(arguments),            fns = this.listenList[key];        if(!fns || fns.length === 0) {            return;        }        for(let i = 0; i < fns.length; i++) {            fns[i].apply(this, arguments);        }    }    removeListen(key, cb) {        let fn = this.listenList[key];        if(!fn) {            return;        }        if(!cb) {   // 没传第二个参数则移除所有函数            fn = [];            return;        }        for (var i = 0; i < fn.length; i++) {            if(fn[i] === cb) {                fn.splice(i, 1);            }        }    }}const test = new Event();let f1, f2, f3;test.addListen('male', f1 = function (msg) {    console.log(msg);})test.addListen('female', f2 = function (msg) {    console.log(msg);})test.addListen('female', f3 = function (msg) {    console.log('female', msg, 1111);})test.removeListen('female', f3);   // 勾销订阅test.trigger('male', '1000000');test.trigger('female', '2000000');