聊一聊发布-订阅模式
参考书籍:《JavaScript设计模式与开发实践》
发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状
态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型来替代传统的发布—订阅模式。
实例走起来:
假如小明和小红都想去学校小卖部买冰激凌,小明想吃绿豆味道,小红想吃红豆味道,此时这两种口味的没有了,老板说,现在没这两种口味了,等有了我电话通知你们再来买,ok,这就是一个简单的订阅模式
让我们用代码来实现以下吧!come on
// 核心超市
let shop={
list:[], //定义订阅中心,用来存储订阅者
listen:function(fn){
this.list.push(fn)
}, //初始化订阅者
trigger:function(){
for (let index = 0; index < this.list.length; index++) {
const element = this.list[index];
element.apply(this,arguments)
}
}, //初始化发布者
}
shop.listen( function( price, taste){ // 小明订阅消息,要买绿豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shop.listen( function( price, taste){ // 小红订阅消息,要买红豆味 console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shop.trigger( 2, '绿豆' );
shop.trigger( 3, '红豆' );
这里候我们发现了问题,小明并不想知道红豆的信息,反之亦然那么怎么办法?
// 核心超市
let shop={
list:{}, //定义订阅中心,用来存储订阅者
listen:function(key,fn){
//如果订阅者的订阅空间不存在则创建
if(!this.list[key]){ this.list[key]=[] }
this.list.push(fn)
}, //初始化订阅者
trigger:function(){
// 取出消息队列的key,数组方法取出第一位参数 let key = Array.prototype.shift.call(arguments) // 取出消息队列的函数 let fns = this.list[key] // 如果不存在则没有订阅者进行订阅 if(!fns || fns.length===0){ return false } for (let index = 0; index < this.list[key].length; index++) { const element = this.list[key][index]; element.apply(this,arguments) }
}, //初始化发布者
}
shop.listen('绿豆', function( price, taste){ // 小明订阅消息,要买绿豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shop.listen('红豆', function( price, taste){ // 小红订阅消息,要买红豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shop.trigger('绿豆', 2, '绿豆' );
shop.trigger('红豆', 3, '红豆' );
此时就不会再订阅到别的信息了,到这里我相信你已经掌握了发布订阅的神髓,那么不妨让我们在发散一下,小明不想去小卖部买了,想去门口超市买,如何解决呢?难道我们要重写一遍代码吗?当然不,作为一门解释型语言,给对象动态添加属性是咱的本分啊!
// 核心超市
let shop={
list:{}, //定义订阅中心,用来存储订阅者
listen:function(key,fn){
//如果订阅者的订阅空间不存在则创建
if(!this.list[key]){
this.list[key]=[]
}
this.list.push(fn)
}, //初始化订阅者
trigger:function(){
// 取出消息队列的key,数组方法取出第一位参数
let key = Array.prototype.shift.call(arguments)
// 取出消息队列的函数
let fns = this.list[key]
// 如果不存在则没有订阅者进行订阅
if(!fns || fns.length===0){
return false
}
for (let index = 0;
index < this.list[key].length; index++) {
const element = this.list[key][index];
element.apply(this,arguments)
}
}, //初始化发布者
}
//如果我想订阅别的超市怎么办呢let initShop=function(obj){ for (const key in shop) { obj[key]=shop[key] }}
// 这时候让我们来个大杯let shopPuls={}initShop(shopPuls)
shopPuls.listen('绿豆', function( price, taste){ // 小明订阅消息,要买绿豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shopPuls.listen('红豆', function( price, taste){ // 小红订阅消息,要买红豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shopPuls.trigger( '绿豆',2, '绿豆' );
shopPuls.trigger( '红豆',3, '红豆' );
这样,小明和小红就可以愉快的吃冰激凌了,过了几天由于冰激凌吃的太多,小明生病了,想取消订阅,这时候怎么办呢,让我们价格remove的方法吧!
// 核心超市
let shop={
list:{}, //定义订阅中心,用来存储订阅者
listen:function(key,fn){
//如果订阅者的订阅空间不存在则创建
if(!this.list[key]){ this.list[key]=[] }
this.list.push(fn)
}, //初始化订阅者
trigger:function(){
// 取出消息队列的key,数组方法取出第一位参数 let key = Array.prototype.shift.call(arguments) // 取出消息队列的函数 let fns = this.list[key] // 如果不存在则没有订阅者进行订阅 if(!fns || fns.length===0){ return false } for (let index = 0; index < this.list[key].length; index++) { const element = this.list[key][index]; element.apply(this,arguments) }
}, //初始化发布者
remove:function(key,fn){ let fns = this.list[key] // 没有订阅者直接返回 if(!fns){ return false } //有订阅者 if(!fn){ //没有回调函数,这标识取消所有的订阅 fns&&(fns.length=0) }else{ for (let index = 0; index < fns.length; index++) { const element = fns[index]; if(element===fn){ fns.splice(index,1) } } } },//取消订阅
}
//如果我想订阅别的超市怎么办呢
let initShop=function(obj){
for (const key in shop) {
obj[key]=shop[key]
}
}
// 这时候让我们来个大杯
let shopPuls={}
initShop(shopPuls)
shopPuls.listen('绿豆', f1=function( price, taste){ // 小明订阅消息,要买绿豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shopPuls.listen('红豆', f2=function( price, taste){ // 小红订阅消息,要买红豆味
console.log( '价格= ' + price );
console.log( '味道= ' + taste); });
shopPuls.remove('红豆',f2)
shopPuls.trigger( '绿豆',2, '绿豆' );
shopPuls.trigger( '红豆',3, '红豆' );
这样就可以取消订阅了!
新手处女作,感谢点赞收藏,大神轻点喷!