使用Object.observe 實現數據綁定


Object.observe API概述

最近,JavaScript的MVC框架在Web開發屆非常盛行。在實現MVC框架的時候,一個非常重要的技術就是數據綁定技術。如果要實現模型與視圖的分離,就必須要使用數據綁定技術。但是,MVC框架的原作者對於數據綁定處理實現得並不如人意,因此,Google公司在ECMAScript中封裝了一個Object.observe API,專用於實現數據綁定處理(目前將其正式使用在V8中)。

Object.observe API可以被稱為一種“可以對任何對象的屬性值修改進行監視的事件處理函數”。

在Firefox瀏覽器中,實現了與之相類似的可以對DOM對象進行觀察的Mutation觀察器

目前為止,Object.observe API已經被strawman proposal所承認,被正式使用在V8中。自11月末開始,已經可以在Chrome Canary與開發者通道中對其進行啟用。

本文介紹Object.observe API中的基本功能及一些代碼示例。

目前為止,Object.observe API中包括如下所示的四個方法:

  • Object.observe:為對象指定監視時調用的回調函數
  • Object.unobserve:移除監視時調用的回調函數
  • Object.deliverChangeRecords:通過回調函數對對象值進行修改
  • Object.getNotifier:獲取Notifier對象

可以觀察到的屬性操作包括以下幾種:

  • new:添加屬性
  • updated:修改屬性值
  • reconfigured:修改屬性設定
  • deleted:刪除屬性

接下來介紹如何使用Object.observe方法。

目前(2012年12月6日)為止,如果要使用Object.observe API,需要使用Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的瀏覽器,同時在chrome://flags/中啟用“啟用實驗性 JavaScript”選項,如下圖所示。

簡單代碼示例

Object.observe方法用於為對象指定監視到屬性修改時調用的回調函數,使用方法如下所示。

Object.observe(obj, callback);

Object.observe方法中使用兩個參數,其中第一個參數值為需要被監視的對象,第二個參數值為監視到屬性修改時調用的回調函數名。可以將各對象的屬性操作時生成的ChangeRecord對象數組設置為回調函數的參數。

ChangeRecord對象擁有type、name、oldValue、object四個屬性,各屬性含義如下所示。

function callback(changes) {
    changes.forEach(function(change) {
        console.log(change.type);     //對屬性進行了什么操作 new/updated/reconfigured/delted
        console.log(change.name);     //屬性名
        console.log(change.oldValue); //修改之前的屬性值
        console.log(change.object);   //被監視的對象
    });
}

使用如下所示的代碼,可以在任何時刻對於對象屬性的上述四種操作(new/updated/reconfigured/delted)進行監視:

var obj = {a: 1};
    Object.observe(obj, output); //為對象指定監視時調用的回調函數

    obj.b = 2; //添加屬性
    obj.a = 2; //修改屬性值
    Object.defineProperties(obj, {a: { enumerable: false}}); //修改屬性設定
    delete obj.b; //刪除屬性  
    function output(change) {
        //回調函數,可以在此處書寫在頁面上的輸出。
    }

頁面運行結果如下圖所示(在Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的瀏覽器中)

創建自定義Notify對象

可以監視到的事件並不局限於以上所述的幾種,可以自定義監視事件。

可以使用Notifier對象來自定義針對對象的可訪問屬性(可使用getter方法或setter方法讀取或設置的屬性)被修改時所觸發的事件。這時,我們需要Object.getNotifier()方法獲取被監視對象的Notifier對象,並使用notify方法進行屬性被修改的通知。

在以下這個示例代碼中,為對象自定義time_updated事件及time_read事件並利用這兩個事件監視對象的私有屬性_time的讀取及修改。

var obj2 = {_time: new Date(0)};
var notifier = Object.getNotifier(obj2); //獲取Notifier對象
Object.defineProperties(obj2, { //設置對象的可訪問屬性 
    _time: {
        enumerable: false,
        configrable: false
    },
    seen: {
        set: function(val) {
            var notifier = Object.getNotifier(this);
            notifier.notify({
                type: 'time_updated', //定義time_updated事件
                name: 'seen',
                oldValue: this._time
            });
            this._time = val;
        },
        get: function() {
            var notifier = Object.getNotifier(this);
            notifier.notify({
                type: 'time_read', //定義time_read事件
                name: 'seen',
                oldValue: this._time
            });
            return this._time;
        }
    }
});
Object.observe(obj2, output); //為對象指定監視時調用的回調函數
//執行屬性操作
var first_time = obj2.seen; //觸發time_read事件
obj2.seen = new Date();      //觸發time_updated事件
var second_time = obj2.seen; //觸發time_read事件

控制回調函數的執行時間

在默認情況下,使用Object.observe API指定的回調函數將在JavaScript腳本代碼執行結束時被調用。因此如果對同一對象的同一屬性執行了多次操作,回調函數中獲取到的各屬性值為最后一個操作結束后的值。將前面這個示例中的代碼稍作修改,對使用Object.observe API進行監視的對象的屬性值連續修改七次(為了避免回調函數的循環調用刪除對time_read事件的監視)。

obj3.seen = new Date(2013, 0, 1, 0, 0, 0); //觸發time_updated事件
  obj3.seen = new Date(2013, 0, 2, 0, 0, 0); //觸發time_updated事件
  obj3.seen = new Date(2013, 0, 3, 0, 0, 0); //觸發time_updated事件
  obj3.seen = new Date(2013, 0, 4, 0, 0, 0); //觸發time_updated事件
  obj3.seen = new Date(2013, 0, 5, 0, 0, 0); //觸發time_updated事件
  obj3.seen = new Date(2013, 0, 6, 0, 0, 0); //觸發time_updated事件
  obj3.seen = new Date(2013, 0, 7, 0, 0, 0); //觸發time_updated事件
    

頁面運行結果如下圖所示(在Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的瀏覽器中)

從這個結果中我們可以看出,在回調函數中獲取到的屬性值為同一個屬性值(2013年1月7日)。為了強制獲取事件觸發后立即設置的屬性值,我們需要使用Object.deliverChangeRecords方法。

在如下所示的代碼中,每次修改了屬性值后,即調用Object.deliverChangeRecords方法立即調用回調函數。

obj4.seen = new Date(2013, 0, 1, 0, 0, 0); //觸發time_updated事件
  Object.deliverChangeRecords(output);       //調用回調函數
  obj4.seen = new Date(2013, 0, 2, 0, 0, 0); //觸發time_updated事件
  Object.deliverChangeRecords(output);       //調用回調函數
  obj4.seen = new Date(2013, 0, 3, 0, 0, 0); //觸發time_updated事件
  Object.deliverChangeRecords(output);       //調用回調函數
  obj4.seen = new Date(2013, 0, 4, 0, 0, 0); //觸發time_updated事件
  Object.deliverChangeRecords(output);       //調用回調函數
  obj4.seen = new Date(2013, 0, 5, 0, 0, 0); //觸發time_updated事件
  Object.deliverChangeRecords(output);       //調用回調函數
  obj4.seen = new Date(2013, 0, 6, 0, 0, 0); //觸發time_updated事件
  Object.deliverChangeRecords(output);       //調用回調函數
  obj4.seen = new Date(2013, 0, 7, 0, 0, 0); //觸發time_updated事件

頁面運行結果如下圖所示(在Chrome Canary或Chrome Dev Channel(25.0.1337.0 dev-m以上)版本的瀏覽器中)

從這個結果中我們可以看出,每次執行Object.deliverChangeRecords方法時都將調用回調函數在頁面中輸出修改后的屬性值。


免責聲明!

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



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