在使用 VUE 框架实现 AOS 系统的重构,因此学习了 MVVM 的实现原理是数据劫持+发布订阅模式,这篇记录下 JavaScript 实现的发布/订阅模式。
一、什么是发布/订阅模式
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
好处:
二、实现发布/订阅模式
三、自定义发布/订阅模式
思路:
一、什么是发布/订阅模式
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
好处:
- 支持简单的广播通信,自动通知所有已经订阅过的对象
- 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性
- 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用
二、实现发布/订阅模式
- 指定一个发布者
- 给发布者添加一个缓冲列表,用于存放回调函数以用于通知订阅者
- 发布消息时,发布者遍历缓存列表,依次触发每个订阅者的回调函数
// 简单的天气订阅
var Weather = {
list: [], // 缓存列表
listen: function(fn) { // 增加订阅者
this.list.push(fn)
},
publish: function() { // 发布消息
for(var i=0,fn; fn=this.list[i++];) {
fn.apply(this,arguments);
}
}
};
// 订阅消息
Weather.listen(function(weather, wind){
console.log('天气:' + weather, '风力:'+ wind);
})
// 发布消息
Weather.publish("晴天","微风"); // 天气:晴天 风力:微风
Weather.publish("雷阵雨","5级风"); // 天气:雷阵雨 风力:5级风
运行结果:三、自定义发布/订阅模式
思路:
- 创建一个对象(缓存列表)
- addlisten 方法用来把订阅回调函数 fn 都加到缓存列表 listenList 中
- publish 方法取到 arguments 里第一个当做key,根据 key 值去执行对应缓存列表中的函数
- remove 方法可以根据 key 值取消订阅
// 升级版天气订阅
var Weather = {
listenList:{}, //缓存对象
// 增加订阅者
addlisten:function(key,fn){
// 没有没有key给个初值避免调用报错
if (!this.listenList[key]) {
this.listenList[key] = [];
}
// 增加订阅者,一个key就是一种订阅类型
this.listenList[key].push(fn);
},
publish:function(){
const key = Array.from(arguments).shift()
const fns = this.listenList[key]
// 这里排除两种特殊情况,第一种为触发的一种从未订阅的类型,第二种订阅后取消了所有订阅的
if(!fns || fns.length===0){
return false;
}
// 发布消息,触发同类型的所有订阅,
fns.forEach((fn)=>{
fn.apply(this,arguments);
})
},
remove:function(key,fn){
var fns=this.listenList[key];//取出该类型的对应的消息集合
if(!fns){//如果对应的key没有订阅直接返回
return false;
}
if(!fn){//如果没有传入具体的回掉,则表示需要取消所有订阅
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
if(fn===fns[l]){ // 这里是传入地址的比较,所以不能直接用匿名函数了
fns.splice(l,1);//删除订阅者的回掉
}
}
}
}
};
// 台风
function taifeng(weather, wind){
console.log("天气:"+ weather +",风力:"+ wind);
}
// 晴天
function qingtian(weather, wind){
console.log("天气:"+ weather +",风力:"+ wind);
}
// 雷阵雨
function leizhenyu(weather, wind){
console.log("天气:"+ weather +",风力:"+ wind);
}
// 订阅了台风通知
Weather.addlisten("台风", taifeng);
// 订阅了晴天通知
Weather.addlisten("晴天", qingtian);
// 订阅了雷阵雨通知
Weather.addlisten("雷阵雨", leizhenyu);
// 取消订阅晴天通知
Weather.remove("晴天", qingtian);
// 发布订阅
Weather.publish("台风","12级");
Weather.publish("晴天","1级");
Weather.publish("雷阵雨","2级");
运行结果:
缺点:
1.状态未知
发布者不知道订阅者的状态,反之亦然,这样的话,你根本不知道在另一端是否会没有问题?
2.不能识别恶意消息
攻击者(恶意的发布者)能够入侵系统并且撕开它。这会导致恶意的消息被发布,订阅者能够获得他们以前并不能获得的消息。
3.关系更新难
更新发布者和订阅者的关系会是一个很难的问题,因为毕竟他们根本不认识对方
参考:
https://segmentfault.com/a/1190000020886430
https://zhaomenghuan.js.org/note/javascript/javascript-pub-sub.html
评论