赞
踩
定义:定义对象之间一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都将得到通知
发布-订阅模式让两个对象松耦合地联系在一起,虽然不太清楚彼此的细节,但这不影响它们之间相互通信。当有新的订阅者出现时,发布者的代码不需要做任何改变,同样发布者需求改变时,也不会影响到之前的订阅者。只要约定的事件名没有变化,就可以自由地改变它们
实现发布-订阅模式
先把发布-订阅的功能提取出来,放在一个单独的对象内
const event={ clientList:{}, listen:function(key,fn){ if(!this.clientList[key]){ this.clientList[key]=[]; } this.clientList[key].push(fn);//订阅的消息加入缓存列表 }, trigger:function(){ const key=Array.prototype.shift.call(arguments); const fns=this.clientList[key]; //如果没有绑定对应的消息 if(!fns||fns.length===0) return false; for(let i=0,fn;fn=fns[i++];){ fn.apply(this,arguments);//arguments是调用trigger时所带上的参数 } } }
再定义一个installEvent函数,这个函数可以给所有对象都动态安装发布-订阅功能
const installEvent=function(obj){
for(let key in event){
obj[key]=event[key];
}
}
测试——给售楼处对象salesOffice动态添加发布-订阅功能
const salesOffices={};
installEvent(salesOffices);
salesOffices.listen('square88',function(price){//小明订阅消息
console.log(`价格:${price}`);
});
salesOffices.listen('square110',function(price){//小红订阅消息
console.log(`价格:${price}`);
});
salesOffices.trigger('square88',880000);//价格:880000
salesOffices.trigger('square110',1100000);//价格:1100000
event.remove=function(key,fn){//取消订阅 const fns=this.clientList[key]; //如果对应的key值没人订阅,直接返回 if(!fns) return false; //如果没有传入回调函数,表示要取消key值对应消息的所有订阅 if(!fn){ fns&&(fns.length=0); }else{ //反向遍历订阅的回调函数列表 for(let i=fns.length-1;i>=0;i--){ if(fns[i]===fn){ //删除订阅者的回调函数 fns.splice(i,1); } } } }
测试:
const fn=function(price){
console.log(`价格:${price}`);
}
salesOffices.listen('square88',fn);//订阅
salesOffices.remove('square88');//取消订阅
发布-订阅模式中,可以用一个全局的Event对象来实现,订阅者不需要了解信息来自哪个发布者,发布者也不知道信息会推送给哪个订阅者,Event作为一个类似“中介”的角色,把发布者和订阅者联系起来
//全局的发布-订阅对象 const Event=(function(){ const clientList={}; //订阅消息 const listen=function(...args){ const key=args[0]; const fn=args[1]; if(!clientList[key]){ clientList[key]=[]; } clientList[key].push(fn);//订阅的消息加入缓存列表 } //发布消息 const trigger=function(){ const key=Array.prototype.shift.call(arguments); const fns=clientList[key]; //如果没有绑定对应的消息 if(!fns||fns.length===0) return 0; for(let i=0,fn;fn=fns[i++];){ fn.apply(this,arguments);//arguments是调用trigger传入的参数 } } //取消订阅 const remove=function(...args){ const key=args[0],fn=args[1]; const fns=clientList[key]; //未订阅,直接返回 if(!fns||fns.length===0) return false; //未传入fn,取消key所有相关的订阅 if(!fn){ fns&&(fns.length=0); }else{ //反向遍历缓存列表 for(let i=fns.length-1;i>=0;i--){ if(fns[i]===fn){ //从缓存列表删除订阅者的回调函数 fns.splice(i,1); } } } } return { listen, trigger, remove } })(); const fn=function(price){ console.log(`价格:${price}`); } Event.listen('square90',fn); Event.listen('square10',fn); Event.remove('square10',fn); Event.trigger('square90',1000); Event.trigger('square10',3000); //输出:价格:1000
点击a模块里的按钮,b模块显示按钮点击次数:
const a=(function(){
let count=0;
const btn=document.querySelector('#btn');
btn.addEventListener('click',function(){
Event.trigger('add',++count);
});
})();
const b=(function(){
const divObj=document.querySelector('#text');
Event.listen('add',function(count){
divObj.innerHTML=count;
})
})();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CfrE5Twv-1637981929666)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20211113113537094.png)]
某些情况下,我们需要先将消息保存下来,等到有对象订阅它的时候,再重新安发布给订阅者。就像QQ的离线消息一样,离线消息先被保存在服务器中,接收人下次登录的时候,可以重新接收到这条消息
为了满足这类需求,可以建立一个存放离线事件的堆栈,事件发布的时候,还没有订阅者来订阅这个事件,就暂时先把发布事件的动作包裹在一个函数里,这些包装函数将会被保存在堆栈中,等到有订阅者订阅消息的时候,再遍历堆栈并且依次执行这些包装函数,也就是重新发布这些事件(离线事件生命周期只有一次,就像qq消息只会被重新阅读一次)
发布-订阅模式,又称观察者模式,在实际开发中非常有用
优点:
缺点:
创建订阅者本身需要消耗一定的时间和内存,而当你订阅一个消息后,也许消息最后都没有发生,但这个订阅者会始终存在于内存中
可以弱化对象之间的联系,如果过度使用的话,对象之间的必要联系会被深埋在背后,会导致程序难以跟踪维护和理解,特别是多个发布者和订阅者嵌套在一起的时候
发布-订阅模式的应用非常广泛,既可以用在异步编程中,也可以帮助我们完成更松耦合的代码编写,还可以用来帮助实现一些别的设计模式,比如中介者模式
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。