js发布订阅模式
订阅-发布模式
再开始之前先简单介绍下订阅-发布模式,因为这种设计模式在vue代码中起到比较关键的作用。
首先,简单来说订阅-发布模式是一种定义一对多依赖关系的设计模式,当一个对象状态发生变化时候,所依赖于他的所有对象都将得到通知。
举个简单例子:很多学生到书店买书,A同学定了一本《js高程》,B同学定了一本《js设计模式》……,但是书店的好多书厂家还没发货,买书者不可能天天跑过去问书到了没,这样书店也应付不过来,买书者也耗费大量时间。所以一般这种情况,书店会留下买书者信息,等书到了通知买书者就ok了。这个例子里,买书者就充当订阅者,书店就是发布者。
既然是订阅-发布模式,那么它应当具备以下特征:
- 有许多订阅者,有一个发布者
- 发布者具有监听订阅者需要订阅什么东西的能力
- 发布者具有发布信息能力,即自身状态改变通知所有订阅者的能力
下面根据上面的特点来实现一个最简单的发布订阅模式 :
// 创建一个发布者
const publisher = {};
//发布者储存订阅者信息的对象
const informations = {}
//给发布者赋予监听订阅者和它需要信息的能力,sbuscriber_key--订阅者标识,fn--订阅者需要的信息或者服务
publisher.listen = (sbuscriber_key,fn) => {
if(!informations[sbuscriber_key] ){ //如果订阅者没有订阅过信息,新建一个数组存储订阅者信息(一个订阅者可能订阅多条信息)
informations[sbuscriber_key] = [];
}
informations[sbuscriber_key].push(fn);
}
//给发布者赋予通知所有订阅者信息的能力
publisher.inform = (sbuscriber_key,...arg) => {
if(!informations[sbuscriber_key] || informations[sbuscriber_key].length === 0){
return new Error('订阅信息不存在')
}
informations[sbuscriber_key].forEach(fn => {
fn.call(this,...arg)
});
}
publisher.listen('A',bookName => {
console.log(bookName+'---到了可以过来取了')
}) //A订阅一本书
publisher.listen('B',(bookName,price) => {
console.log(bookName+'---到了,价格'+price+'是否还要')
}) //B订阅一本书
//书到了,发布者通知订阅者
publisher.inform('A','js高程') //js高程---到了可以过来取了
publisher.inform('B','js设计模式',50) //js设计模式---到了,价格50是否还要
上面的例子里,比如B同学要到很多书店买书看哪个书店到货了,最后要确切知道去哪个书店买书,造成了B同学和书店的耦合,所以这时候班级统一找一个人C登记每个人要卖的书。买书的不用知道去哪个书店买的,书店也不需要知道卖给谁了,统一由C代理,这样C就是该事件的发布者,根据第一个代码实现,把发布者做一个通用实现:
function Publisher(){
this.informations = {}
}
Publisher.prototype = {
constructor : Publisher,
inform: function (sbuscriber_key,...arg){
if(!this.informations[sbuscriber_key] || this.informations[sbuscriber_key].length === 0){
return new Error('订阅信息不存在')
}
this.informations[sbuscriber_key].forEach(fn => {
fn.call(this,...arg)
})
console.log(this)
},
listen: function (sbuscriber_key,fn){
if(!this.informations[sbuscriber_key] ){
this.informations[sbuscriber_key] = [];
}
this.informations[sbuscriber_key].push(fn);
},
remove:function (sbuscriber_key){
if(!this.informations[sbuscriber_key] ){
return;
}
this.informations[sbuscriber_key].length = 0;
}
}
const p = new Publisher();
p.listen('A',bookName => {
console.log(bookName+'---到了可以过来取了')
})
p.listen('B',(bookName,price) => {
console.log(bookName+'---到了,价格'+price+'是否还要')
})
p.inform('A','js高程')
p.inform('B','js设计模式',50)
给出通用构造函数Pulisher