Appearance
全局的发布订阅对象
回想下刚刚实现的发布—订阅模式,我们给售楼处对象和登录对象都添加了订阅和发布的功 能,这里还存在两个小问题。
我们给每个发布者对象都添加了 listen 和 trigger 方法,以及一个缓存列表 clientList, 这其实是一种资源浪费。
小明跟售楼处对象还是存在一定的耦合性,小明至少要知道售楼处对象的名字是 salesOffices,才能顺利的订阅到事件。见如下代码:
javascript
salesOffices.listen( 'squareMeter100', function( price ){ // 小明订阅消息
console.log( '价格= ' + price );
});如果小明还关心 300 平方米的房子,而这套房子的卖家是 salesOffices2,这意味着小明要开 始订阅 salesOffices2 对象。见如下代码:
javascript
salesOffices2.listen( 'squareMeter300', function( price ){ // 小明订阅消息
console.log( '价格= ' + price );
});其实在现实中,买房子未必要亲自去售楼处,我们只要把订阅的请求交给中介公司,而各大 房产公司也只需要通过中介公司来发布房子信息。这样一来,我们不用关心消息是来自哪个房产 公司,我们在意的是能否顺利收到消息。当然,为了保证订阅者和发布者能顺利通信,订阅者和 发布者都必须知道这个中介公司。
同样在程序中,发布—订阅模式可以用一个全局的 Event 对象来实现,订阅者不需要了解消 息来自哪个发布者,发布者也不知道消息会推送给哪些订阅者,Event 作为一个类似“中介者” 的角色,把订阅者和发布者联系起来。见如下代码:
javascript
var Event = (function () {
var clientList = {},
listen,
trigger,
remove;
listen = function (key, fn) {
if (!clientList[key]) {
clientList[key] = [];
}
clientList[key].push(fn);
};
trigger = function () {
var key = Array.prototype.shift.call(arguments),
fns = clientList[key];
if (!fns || fns.length === 0) {
return false;
}
for (var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
};
remove = function (key, fn) {
var fns = clientList[key];
if (!fns) {
return false;
}
if (!fn) {
fns && (fns.length = 0);
} else {
for (var l = fns.length - 1; l >= 0; l--) {
var _fn = fns[l];
if (_fn === fn) {
fns.splice(l, 1);
}
}
}
};
return {
listen: listen,
trigger: trigger,
remove: remove
}
})();
Event.listen('squareMeter88', function (price) { // A 订阅消息
console.log('价格= ' + price);
});
Event.trigger('squareMeter88', 2000000); // 售楼处发布消息