乐趣区

关于javascript:发布订阅

公布订阅

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

举个例子

你网购一个洗面奶,然而那家店目前断货了,于是你每天都到那家店询问货到否,和你一样的购物者(假如有 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');
退出移动版