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 提供了基本的觀察者模式支持。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM