什么是发布-订阅模式?
发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
现实生活中的发布-订阅模式
小梁、小花、小红都喜欢上同一家淘宝店铺的同一件衣服,当她们正要下单的之后被店家通知这件衣服没货了。小梁问店家什么时候有货,店家表示得看加工厂的工作进度,具体的上架日期还不确定,但表示可以加入他们家的粉丝群,等到衣服有货了会第一时间通知进群的粉丝。于是小梁、小花、小红都加入该店铺的粉丝群。一周后,店家在粉丝群艾特所有人通知大家衣服重新上架了,小梁、小花、小红第一时间收到了这条消息,高兴地买到了心仪已久的衣服。
发布-订阅模式的作用
- 发布-订阅模式可以广泛应用于异步编程中,这是一种替代回调函数的方案。比如,我们可以订阅ajax请求中的success 、error等事件,无需过多关注对象在异步运行期间的内部状态,而只需订阅感兴趣的事件发生点。
- 可以取代对象之间硬编码的通知机制,一个对象不再显式地调用另外一个对象的某个接口。当有新的订阅者出现时,发布者的代码不需要任何修改;同样发布者需要改变时,也不会影响到之前的订阅者。只要之前约定的事件名没有变化,就可以自由地改变它们。
- 支持简单的广播通信,当对象状态发生改变时,会自动通知已经订阅过的对象。
发布-订阅模式的实现
- 确定发布者(店家);
- 给发布者添加一个缓存列表,用于存放回调函数来通知订阅者;
- 当发布消息时,发布者遍历这个缓存列表,依次触发里面存放的订阅者回调函数。
另外,还可以往回调函数里填入一些参数,订阅者可以接受到这些参数。
下面是一个简单的发布-订阅模式:
//1.指定谁充当发布者(淘宝店家)
let clothesShop = {
};
//2.缓存列表,存放订阅者的回调函数
clothesShop.clientList = [];
//增加订阅者
clothesShop.listen = function (fn) {
this.clientList.push(fn); //订阅的消息添加进缓存列表中
};
//3.发布消息,遍历缓存列表
clothesShop.trigger = function () {
for(let i = 0,fn;fn = this.clientList[i++];){
fn.apply(this,arguments); //arguments是发布消息时带上的参数
}
};
//测试
//小梁订阅消息
clothesShop.listen(function (color,size) {
console.log("颜色是:"+color);
console.log("尺码是:"+size);
});
//小花订阅消息
clothesShop.listen(function (color,size) {
console.log("颜色是:"+color);
console.log("尺码是:"+size);
});
//发布消息
//蓝色衣服上架通知所有买家
clothesShop.trigger("blue","s");
//粉色衣服上架通知所有买家
clothesShop.trigger("pink","m");
虽然这是最简单的发布-订阅模式,但是我们可以发现订阅者接收到了发布者发布的所有消息。其实,小梁只是想买s码的蓝色衣服,小花想买m码的粉色衣服,有没有一种办法可以根据订阅者需要发布订阅者需要的消息呢?
于是我们可以增加一个标示key,让订阅者只订阅自己感兴趣的消息。改写代码如下:
//1.指定谁充当发布者(淘宝店家)
let clothesShop = {
};
//2.缓存列表,存放订阅者的回调函数
clothesShop.clientList = [];
//增加订阅者
clothesShop.listen = function (key,fn) {
if(!this.clientList[key]){
this.clientList[key] = []; //如果还没有订阅过此条消息,则给该类消息创建一个缓存列表
}
this.clientList[key].push(fn); //订阅的消息添加进消息缓存列表
};
//3.发布消息,遍历缓存列表
clothesShop.trigger = function () {
let key = Array.prototype.shift.call(arguments); //取出消息类型
let fns = this.clientList[key];
if(!fns || fns.length == 0){
//如果没有订阅该消息,则返回
return false;
}
for(let i = 0,fn;fn = fns[i++];){
fn.apply(this,arguments); //arguments是发布消息时带上的参数
}
};
//测试
//小梁订阅消息
clothesShop.listen("小梁",function (color,size) {
console.log("这是小梁订阅的:");
console.log("颜色是:"+color);
console.log("尺码是:"+size