JS设计模式--发布订阅模式

2018年01月27日Web前端

继续设计模式,这次讲的是更为常用的发布订阅模式,又称为观察者模式。

一、什么是发布订阅模式

其实,我们常用的事件模型就是很好的发布订阅模式的例子。

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事件。

四、总结

发布订阅模式的优势十分明显,一是时间上的解解耦,二是对象之间的解耦。并且在异步编程中帮我们完成松耦合的代码编写。当然问题也有,订阅的事件会一直存在内存中,而且过度使用的话,嵌套过深,定位问题的话,也会变得很复杂。