JS设计模式--发布订阅模式
继续设计模式,这次讲的是更为常用的发布订阅模式,又称为观察者模式。
一、什么是发布订阅模式
其实,我们常用的事件模型就是很好的发布订阅模式的例子。
document.getElementById('btn').addEventListener('click', function () {
alert();
}, false);
// 模拟用户点击
// click
id是btn的按钮订阅了click事件,当用户点击按钮的时候,便会触发事件回调。
二、发布订阅的作用
发布订阅模式可以广泛用于异步编程中,我们无需关注对象在异步运行期间的内部状态。
发布订阅模式可以取代对象之间的通知机制,该模式可以让两个对象松耦合地联系在一起。
三、发布订阅模式的实现
1.基本实现
我们自己实现一个发布订阅模式:
var event = {
eventList: [],
add: function (name, fn) {
if (!this.eventList[name]) {
this.eventList[name] = [];
}
// 增加订阅事件队列
this.eventList[name].push(fn);
},
trigger: function () {
var name = arguments[0], // 触发的事件名
args = Array.prototype.slice.call(arguments, 1),
fns = this.eventList[name]; // 出发执行的函数
if (!fns || 0 === fns.length) {
// 没有订阅过该事件
return;
}
for (var i = 0, fn; fn = fns[i++]; ) {
fn.apply(this, args)
}
}
};
此时,我们可以对该对象增加对于的功能了:
// 增加open事件
event.add('open', function (a) {
console.log(a);
});
event.trigger('open', 'open');
2.取消订阅事件
event.remove = function (name, fn) {
var fns = this.eventList[name];
if (!fns) { // 没有订阅的事件
return;
}
if (!fn) { // 没有传入具体的回调函数,取消所有的事件
fns && (fns.length = 0);
} else {
for (var i = fns.length - 1; i >= 0; --i) {
var _fn = fns[i];
if (_fn === fn) {
fns.splice(i, 1);
}
}
}
}
event.remove('open'),这样我们就删除了前次发布的所有open事件。
四、总结
发布订阅模式的优势十分明显,一是时间上的解解耦,二是对象之间的解耦。并且在异步编程中帮我们完成松耦合的代码编写。当然问题也有,订阅的事件会一直存在内存中,而且过度使用的话,嵌套过深,定位问题的话,也会变得很复杂。