观察者模式,又称发布-订阅模式或消息机制,定义了一种依赖关系,解决了主题对象与观察者之间功能的耦合。
通过运用观察者模式,可以解决团队开发中的模块间通讯问题,这是模块间解耦的一种可行方案。
首先,我们来把观察者对象创建处理,它有一个消息容器和三个方法,分别是订阅消息方法、发送订阅消息的方法和取消订阅消息的方法。如下:
// 将观察者放在闭包中,当页面加载就立即执行 var Observer = (function () { // 将消息容器做为静态私有变量,防止消息队列泄露而被篡改 var _messages = {}; return { regist: function () {}, // 注册消息 fire: function () {}, // 发布消息 remove: function () {} // 取消消息 }; })();
现在,观察者的雏形已经出来了,我们接下来的事情就是一步步实现这三个方法。
1. 注册消息的作用是将订阅者的消息推入到消息队列中,因此我们需要接受两个参数:消息类型和相应的处理动作。
在推入到消息队列时,如果此消息不存在则应该创建一个消息类型并将该消息放入消息队列中;如果此消息存在则应该将消息执行方法推入改消息对应的执行方法队列中,这么做的目的是保证多 个模块注册同一则消息时能顺利执行。
regist: function (type, fn) { if (typeof _messages[type] === 'undefined') { _messages[type] = [fn]; // 若此消息不存在,则创建一个消息容器 } else { _messages[type].push(fn); // 消息存在,则将动作方法推入该消息对应的动作执行序列中 } }
2. 发布消息的功能是,当观察者发布一个消息时,会将所有订阅者订阅的消息依次执行。故应该接受两个参数:消息类型和动作执行时要传递的参数。
在执行消息动作队列之前对消息存在的校验是很有必要的,然后遍历消息执行方法队列,并依次执行。
fire: function (type, args) { if (!_messages[type]) { return; }; var events = { type: type, args: args || {} }; for (var i = 0, len = _messages[type].length; i < len; i++) { _messages[type][i].call(this, args); }; },
3. 注销消息的功能是将订阅者注销的消息从消息队列中清除掉,因此我们可以传递两个参数:消息类型和动作函数。
如果只传递消息类型参数,则注销所有订阅此消息类型的动作函数,
若两者都传递,则只注销此消息类型的当前传递参数的动作函数。
当然为了避免删除消息动作时消息不存在的情况出现,对消息队列中消息的存在性校验也是很有必要的。
remove: function (type, fn) { if (!fn) { _messages[type] = null; return; }; if (_messages[type] instanceof Array) { var i = _messages[type].length - 1; for (; i >= 0; i--) { _messages[type][i] === fn && _messages[type].splice(i, 1); }; } }
至此,观察者模式就实现完啦!可以稍微测试一下:
var fn1 = function () { console.log(1); } var fn2 = function () { console.log(2); } Observer.regist('test', fn1); Observer.regist('test', fn2); Observer.fire('test'); // 1 2 Observer.remove('test', fn1); Observer.fire('test'); // 2