冠军

导航

Kendo UI - Observable

 在 Kendo 中,基类 Class 第一个重要的派生类就是 Observable, 顾名思义,就是一个可观察的对象,也就是观察者模式的基础。

对于观察者模式来说,应该有主题和观察者,这里我们讨论的其实是主题,观察者只需要提供一个回调函数,在适当的时候得到回调就可以了。

对于主题来说,我们应该支持多种观察的目标,如果你使用过 .NET 的事件,这里简直就是将 .NET 的事件轮子重新实现了一下。

1.事件

_events 是用来保存注册的事件信息的存储对象,可以在主题上定义多种事件,每个事件就是 _events 上的一个字段,字段的名字就是事件名称,值是一个数组,用来保存注册到这个事件的回调函数。初始化函数中,将这个对象创建出来。

init: function() {
   this._events = {};
},

2. 注册

bind 函数用来进行注册,既可以使用事件名称,处理器方式,也可以一次性注册多个事件,使用事件名称的数组,对应每个事件的处理函数对象来表示。

最关键的实际上是这两行。

 events = that._events[eventName] = that._events[eventName] || [];
 events.push(handler);

这里有一些特殊的处理,就是可以注册仅仅执行一次的处理器。在注册的时候,需要将 one 设置为 true,默认是 undefined,也就是多次的。

在一次的情况下,会自动将用户注册的处理器另外保存到 original 中,然后创建一个新的处理器进行注册,这个处理器在执行一次之后,自动将自己从处理器列表中删除。

注册的全部代码

bind: function(eventName, handlers, one) {
    var that = this,
        idx,
        eventNames = typeof eventName === STRING ? [eventName] : eventName,
        length,
        original,
        handler,
        handlersIsFunction = typeof handlers === FUNCTION,
        events;

    if (handlers === undefined) {
        for (idx in eventName) {
            that.bind(idx, eventName[idx]);
        }
        return that;
    }

    for (idx = 0, length = eventNames.length; idx < length; idx++) {
        eventName = eventNames[idx];

        handler = handlersIsFunction ? handlers : handlers[eventName];

        if (handler) {
            if (one) {
                original = handler;
                handler = function() {
                    that.unbind(eventName, handler);
                    original.apply(that, arguments);
                };
                handler.original = original;
            }
            events = that._events[eventName] = that._events[eventName] || [];
            events.push(handler);
        }
    }

    return that;
},

 

 

3. 取消注册

对应注册的就是取消注册了。

unbind 完成取消注册的任务,取消注册的时候,有三种选择

  • 全部取消注册的观察者
  • 将某个时间的观察者取消
  • 或者单个取消

所以代码更加简单明了。original 就是在一次性事件中保存的原有处理器。

unbind: function(eventName, handler) {
    var that = this,
        events = that._events[eventName],
        idx;

    if (eventName === undefined) {
        that._events = {};
    } else if (events) {
        if (handler) {
            for (idx = events.length - 1; idx >= 0; idx--) {
                if (events[idx] === handler || events[idx].original === handler) {
                    events.splice(idx, 1);
                }
            }
        } else {
            that._events[eventName] = [];
        }
    }

    return that;
}

 

4. 触发处理

触发就比较容易了,提供事件的名称,事件的参数就可以了,直接遍历数组中保存的每一个处理器,通过 call 调用将对象自己作为 this 传递到处理器中。

trigger: function(eventName, e) {
    var that = this,
        events = that._events[eventName],
        idx,
        length;

    if (events) {
        e = e || {};

        e.sender = that;

        e._defaultPrevented = false;

        e.preventDefault = preventDefault;

        e.isDefaultPrevented = isDefaultPrevented;

        events = events.slice();

        for (idx = 0, length = events.length; idx < length; idx++) {
            events[idx].call(that, e);
        }

        return e._defaultPrevented === true;
    }

    return false;
}

 

5. 辅助函数

额外还提供了两个辅助函数,one 和 first

one 用来检查注册仅仅执行一次的处理器,你会看到通过直接将 bind 的 one 参数设置为 true 来实现的。

one: function(eventNames, handlers) {
    return this.bind(eventNames, handlers, true);
},

 

first 用来将处理函数压入调用对象的最前面, unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。

first: function(eventName, handlers) {
    var that = this,
        idx,
        eventNames = typeof eventName === STRING ? [eventName] : eventName,
        length,
        handler,
        handlersIsFunction = typeof handlers === FUNCTION,
        events;

    for (idx = 0, length = eventNames.length; idx < length; idx++) {
        eventName = eventNames[idx];

        handler = handlersIsFunction ? handlers : handlers[eventName];

        if (handler) {
            events = that._events[eventName] = that._events[eventName] || [];
            events.unshift(handler);
        }
    }

    return that;
},

 

 

6. 全部代码

全部代码如下:

var Observable = Class.extend({
    init: function() {
        this._events = {};
    },

    bind: function(eventName, handlers, one) {
        var that = this,
            idx,
            eventNames = typeof eventName === STRING ? [eventName] : eventName,
            length,
            original,
            handler,
            handlersIsFunction = typeof handlers === FUNCTION,
            events;

        if (handlers === undefined) {
            for (idx in eventName) {
                that.bind(idx, eventName[idx]);
            }
            return that;
        }

        for (idx = 0, length = eventNames.length; idx < length; idx++) {
            eventName = eventNames[idx];

            handler = handlersIsFunction ? handlers : handlers[eventName];

            if (handler) {
                if (one) {
                    original = handler;
                    handler = function() {
                        that.unbind(eventName, handler);
                        original.apply(that, arguments);
                    };
                    handler.original = original;
                }
                events = that._events[eventName] = that._events[eventName] || [];
                events.push(handler);
            }
        }

        return that;
    },

    one: function(eventNames, handlers) {
        return this.bind(eventNames, handlers, true);
    },

    first: function(eventName, handlers) {
        var that = this,
            idx,
            eventNames = typeof eventName === STRING ? [eventName] : eventName,
            length,
            handler,
            handlersIsFunction = typeof handlers === FUNCTION,
            events;

        for (idx = 0, length = eventNames.length; idx < length; idx++) {
            eventName = eventNames[idx];

            handler = handlersIsFunction ? handlers : handlers[eventName];

            if (handler) {
                events = that._events[eventName] = that._events[eventName] || [];
                events.unshift(handler);
            }
        }

        return that;
    },

    trigger: function(eventName, e) {
        var that = this,
            events = that._events[eventName],
            idx,
            length;

        if (events) {
            e = e || {};

            e.sender = that;

            e._defaultPrevented = false;

            e.preventDefault = preventDefault;

            e.isDefaultPrevented = isDefaultPrevented;

            events = events.slice();

            for (idx = 0, length = events.length; idx < length; idx++) {
                events[idx].call(that, e);
            }

            return e._defaultPrevented === true;
        }

        return false;
    },

    unbind: function(eventName, handler) {
        var that = this,
            events = that._events[eventName],
            idx;

        if (eventName === undefined) {
            that._events = {};
        } else if (events) {
            if (handler) {
                for (idx = events.length - 1; idx >= 0; idx--) {
                    if (events[idx] === handler || events[idx].original === handler) {
                        events.splice(idx, 1);
                    }
                }
            } else {
                that._events[eventName] = [];
            }
        }

        return that;
    }
});

 7. 总结 

Observable 提供了基本的观察者模式支持。

 

posted on 2015-11-19 09:55  冠军  阅读(2779)  评论(1编辑  收藏  举报