公布订阅
定义了对象间的一种 一对多 的依赖关系,当一个对象的状态产生扭转时,所依赖它的对象都将失去告诉
举个例子
你网购一个洗面奶,然而那家店目前断货了,于是你每天都到那家店询问货到否,和你一样的购物者(假如有 1000)个,每天都问雷同的问题,这样是不好了,商家想了一个方法,留下你们必要的信息,货到时一一告诉你们。那么咱们就能够把商家当作 发布者
,购物者就是 订阅者
作用:
- 购物者不必每天都向商家询问,商家也不必每天都回复,只须要在适当的工夫,告诉购物者货到就行
- 购物者和商家不毕强耦合,购物者的减少只须要留下必要的信息,商家不须要理解购物者的其余状况。商家的任何变动也不会影响购物者
实现
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');