观察者模式创建自定义事件
简述观察者模式
观察者模式又称发布-订阅模式,主要做“订阅”、“发布”、“撤销订阅”三种操作,事件处理系统就是这种模式的一个实现。
被观察者定义一个缓存,保存订阅者的处理函数。当有新的消息发布,被观察会去检索缓存看有没有观察者订阅这个消息,有的话调用对应的处理函数。
观察者模式使得观察者和被观察者相互分离,避免了相互调用的紧耦合状态。从效果上看,实现相同的目的,观察者模式可以避免过多的条件分支,同时订阅发布的频率难以影响代码的复杂度。
观察者模式在JS中的实现
首先定义一个观察者模式对象(Observer),观察者有“发布”、“订阅”、“撤销”三种操作以及一个保存订阅处理函数的缓存。缓存保存类型与回调函数的最终结构如下:
{
type1 : [callback1, callback2,...],
type2 : [callback3],
...
}
整个对象定义如下:
var Observer = { _cachePool : {}, fire : function(type, msg){},//发布函数,传递发布类型以及相应消息 on : function(type, callback){},//订阅函数,传递订阅类型与处理函数 un : function(type, callback){}//解订函数,传递要取消某类型及某处理函数 };
再看发布函数。发布函数做的事是找到缓存池里对应类型的所有处理函数,然后依次执行一遍这些处理函数。
Observer.fire = function(type, msg) { var fns = this._cachePool[type], len = fns.length; for(var i = 0, fn; i < len; i++) { fn = fns[i]; fn.call(this, msg); } };
接着看订阅函数。订阅函数做的事是把订阅的类型与回调函数保存进_cachePool中。先判断缓存中有该类型的空间没,没有的话创建该类型空间再保存回调函数,有的话直接保存。
Observer.on = function(type, callback) { if(!this._cachePool[type]) { this._cachePool[type] = []; } this._cachePool[type].push(callback); };
最后看解除订阅。它做的事是把缓存池中相应类型的回调函数删除。
Observer.un = function(type, callback) { var fns = this._cachePool[type]; if(fns) { this._cachePool[type] = fns.filter(function(item){ return item !== callback; }); } };
解订函数删除相应回调函数用到Array.prototype.filter函数,这个函数属于Ecma 5新增内容。古董浏览器没有这个函数,我们可以自己写一个兼容的filter:
var filter = function(arr, callback) { var resultArr = []; for(var i = 0, len = arr.length; i < len; i++) { if(callback.call(arr[i], arr[i], i)) { resultArr.push(arr[i]); } } return resultArr; }
当然,我们可以扩展Array:Array.prototype.filter = Array.prototype.filter || filter;这样就不用调整解订函数filter的用法了。
观察者模式测试如下:
Observer.on('click', onclick); Observer.on('click', function(msg) {console.log('click...拉姆达' + " " + msg)}); Observer.on('click', onclick); Observer.fire('click', "hello world!"); Observer.un('click', onclick); console.log("解订后:"); Observer.fire('click', "hello world!"); //结果: click..hello world! click...拉姆达 hello world! click..hello world! 解订后: click...拉姆达 hello world!
简易自定义事件达成!
当然,你可以用原生事件处理你新定义的事件,只要实现订阅好事件,然后在原生事件里发布该事件的消息即可。后面的简易富文本编辑器(二)中会具体实践。