现实生活中的发布-订阅模式
不论是在程序世界里还是现实生活中,发布—订阅模式的应用都非常广泛。我们先看一下现实中的例子。
小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼MM告诉小明,不久后还有一些尾盘推出。开发商正在办理相关手续,手续办好后便可以购买。但到底是什么时候,目前还没有人能够知道。
于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是已经到了购买时间。除了小明,还有小红,小强,小龙也会每天向售楼处咨询这个问题。一个星期过后,售楼MM决定辞职,因为厌倦了每天回答1000个相同的电话。
当然现实中没有这么笨的销售公司,实际上故事是这样的:小明离开之前,把电话号码留在售楼处。售楼MM答应他,新楼盘一推出就马上发信息通知小明。小红、小强、小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼MM会翻开花名册,遍历上面的电话号码,依次发送一条短信通知他们。
DOM事件
实际上我们之前就一直在使用发布订阅模式,当我们绑定一个DOM事件的时候,其实就是在使用这个模式,思考下面代码:
document.body.addEventListener(‘click’,function(){
alert(2);
},false);
document.body.click();
- 1
- 2
- 3
- 4
这里需要监控用户点击document.body的动作,但是我们没办法预知用户将在什么时候点击。所以我们订阅document.body上的click事件,当body节点被点击时,body节点便会向订阅者发布这个消息。
发布订阅模式的优点
-
可以广泛应用于异步编程,它可以代替我们传统的回调函数,我们不需要关注对象在异步执行阶段的内部状态,我们只关心事件完成的时间点。
-
取代对象之间硬编码通知机制,一个对象不必显式调用另一个对象的接口,而是松耦合的联系在一起
虽然不知道彼此的细节,但不影响相互通信。更重要的是,其中一个对象改变不会影响另一个对象。
模式的通用实现
var Event = (function(){
var list = {},
listen,
trigger,
remove;
listen = function(key,fn){ //监听事件函数
if(!list[key]){
list[key] = []; //如果事件列表中还没有key值命名空间,创建
}
list[key].push(fn); //将回调函数推入对象的“键”对应的“值”回调数组
};
trigger = function(){ //触发事件函数
var key = Array.prototype.shift.call(arguments); //第一个参数指定“键”
msg = list[key];
if(!msg || msg.length === 0){
return false; //如果回调数组不存在或为空则返回false
}
for(var i = 0; i < msg.length; i++){
msg[i].apply(this, arguments); //循环回调数组执行回调函数
}
};
remove = function(key, fn){ //移除事件函数
var msg = list[key];
if(!msg){
return false; //事件不存在直接返回false
}
if(!fn){
delete list[key]; //如果没有后续参数,则删除整个回调数组
}else{
for(var i = 0; i < msg.length; i++){
if(fn === msg[i]){
msg.splice(i, 1); //删除特定回调数组中的回调函数
}
}
}
};
return {
listen: listen,
trigger: trigger,
remove: remove
}
})();
var fn = function(data){
console.log(data + '的推送消息:xxxxxx......');
}
Event.listen('a', fn);
Event.trigger('a', '2016.11.26');
Event.remove('a', fn);