观察者模式创建自定义事件

简述观察者模式

  观察者模式又称发布-订阅模式,主要做“订阅”、“发布”、“撤销订阅”三种操作,事件处理系统就是这种模式的一个实现。

  被观察者定义一个缓存,保存订阅者的处理函数。当有新的消息发布,被观察会去检索缓存看有没有观察者订阅这个消息,有的话调用对应的处理函数。

  观察者模式使得观察者和被观察者相互分离,避免了相互调用的紧耦合状态。从效果上看,实现相同的目的,观察者模式可以避免过多的条件分支,同时订阅发布的频率难以影响代码的复杂度。

观察者模式在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!

  简易自定义事件达成!

  当然,你可以用原生事件处理你新定义的事件,只要实现订阅好事件,然后在原生事件里发布该事件的消息即可。后面的简易富文本编辑器(二)中会具体实践。

  

原文地址:https://www.cnblogs.com/longhx/p/5440178.html