发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖于它的对象都将得到通知。
举个栗子
比如售楼中心,因为房源紧张,小红、小明、老王每天都要打电话到售楼前台询问今天是否有新楼盘,这样售楼前台每天就需要接收很大量的咨询。
发布-订阅模式就是,售楼前台MM将每个人的电话都记录下来,当有新楼盘的时候,就遍历一遍这些电话号码,依次打电话通知。其中售楼前台是发布者,小红、小明、老王是订阅者。
优点:
- 在合适的时间发布者会通知订阅者。这说明发布-订阅模式可以广泛应用于异步编程中,可以替代回掉函数的方式,无需过多关心异步运行期间的内部状态,只需要订阅感兴趣的事件发生点。
- 减少发布者和订阅者之间的耦合,发布者只记录一个订阅者的电话,并不关心订阅者是女人还是猴子。这个优点说明可以订阅-发布模式可以取代对象之间硬编码的通知机制,不需要再显式的调用另一个对象的某个接口,这让对象之间松耦合的联系在一起,当新订阅者出现的时候,发布者代码不需要作任何修改,同理,当发布者需求改变时也不会影响到之前的订阅者。
典型应用场景
- 时间解耦:应用于异步编程
- 对象解耦:接口之间调用不再显示的进行
发布-订阅模式通用实现
var event = {
clientList: [], //缓存列表,存放订阅者的回掉函数
// 增加订阅者
addListen: function (key, fn) { // key是订阅的消息类型,fn是回调函数
if (!this.clientList[key]) {
this.clientList[key] = []; // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
}
this.clientList[key].push(fn); // 订阅的消息添加进消息缓存列表
},
// 发布消息
emit: function () {
var key = Array.prototype.shift.call(arguments), // 取出消息类型
fns = this.clientList[key]; // 取出该类型的消息对应的回调函数组成的数组
if(!fns || fns.length === 0) {
return false;
}
for (var i=0,fn; fn = fns[i++]) {
fn.apply(this, arguments); //arguments是发布消息时附送的参数
}
}
}
再定义一个 installEvent 函数,这个函数可以给所有的对象都动态安装发布—订阅功能:
var installEvent = function( obj ){ for ( var i in event ){ 给obj对象安装发布-定义模式
obj[ i ] = event[ i ]; }
};
给售楼处对象增加发布-订阅功能:
var salesOffice = {};
installEvent(salesOffice); // 添加订阅-发布完毕
// 对象小明订阅消息
salesOffice.addListen('square88', function(price) {
console.log( '价格= ' + price );// 回调函数
});
// 对象小红订阅消息
salesOffice.addListen('square100', function(price) {
console.log( '价格= ' + price );// 回调函数
});
// 发布消息
salesOffices.emit( 'square88', 2000000 ); // 输出2000000
salesOffices.emit( 'square100', 3000000 ); // 3000000