1. 程式人生 > >玩轉Javascript函式上下文繫結——Dojo hitch/partial詳解

玩轉Javascript函式上下文繫結——Dojo hitch/partial詳解

原文作者:Tom Trenka

譯者:Ruan Qi

Dojo 1.7  難度級別:中級

在dojo工具包中,dojo/_base/lang模組對Javascript原生方法進行了包裝或增強,提供了不少相當有用的方法。本文將介紹Javascript中的函式物件(Function)的基礎知識,以及如何使用dojo.hitch繫結函式的上下文,另外還將介紹如何使用dojo.partial為函式繫結特定引數等實用技術。

在講解dojo.hitch和dojo.partial的用法之前,要先明白它們能解決什麼問題。Javascript程式設計中最常聽到的問題之一:this是什麼?對面向物件程式設計來說,方法內部的this就是宣告該方法的物件,非常簡單!而對於Javascript,答案可不那麼簡單了。為求知其然並知其所以然,有必要了解Javascript中的函式上下文。

Javascript函式上下文

每當一個函式被呼叫時,Javascript會建立一個對應的上下文。上下文建立步驟如下:

  1. 建立argument物件;
  2. 建立函式的scope物件;
  3. 初始化函式的區域性變數;
  4. 建立this屬性;

其中this指向的是正在呼叫該函式的物件。理解這一點對於理解Javascript中的函式執行非常關鍵,因為Javascript中函式的上下文是在函式被呼叫的時候才確定的。

這裡舉一個例子:有一個object,這個object其中一個方法被當做多個HTML節點的點選事件的處理函式。下面是對應的程式碼。

        var myObject = {
            foo: "bar",
            myHandler: function(evt){
                //  this is very contrived but will do.
                alert("The value of 'foo' is " + this.foo);
            }
        };
 
        //  later on in the script:
        dojo.query(".myNodes").forEach(function(node){
            node.onclick = myObject.myHandler;
        });
請看對應的Demo(http://dojotoolkit.org/documentation/tutorials/1.7/hitch/demo/demo.html

當這些class為“myNodes”的button被點選時,你可能會認為瀏覽器會彈出“The value of 'foo' is bar”的訊息,然而由於上面的myObject.myHandler綁定了節點的點選事件,導致瀏覽器彈出了“The value of 'foo' is undefined”。這是因為myObject.myHandler被作為節點的上下文執行,並非被myObject自己執行。換句話說,Javascript直譯器認為當前的this指向了被點選的節點而非myObject。

使用apply、call指定函式執行上下文

Javascript為函式物件提供了apply與call方法,用於切換函式的執行時上下文。兩種方法都可以通過傳入執行時上下文,從而顯式地指定this屬性的引用。舉例來說,我們希望上例中的節點被點選時,觸發的myObject.myHandler的上下文為myObject。這裡使用call方法為例:

dojo.query(".myNodes").forEach(function(node){
    node.onclick = function(evt){
        return myObject.myHandler.call(myObject, evt);
    };
});

理解了Javascript函式的上下文基礎後,來看看dojo如何使用dojo.hitch來簡化這一過程。

使用dojo.hitch繫結函式執行時上下文

Dojo.hitch是dojo提供的一個用來簡化函式執行時上下文繫結的函式。簡單來說dojo.hitch建立了一個綁定了上下文的新函式,使用者可以不用擔心心執行時的上下文變化會對函式產生影響。使用dojo.hitch也十分簡單:
        var foo = "bar"
        var myFunction = function(){
            return this.foo;
        };
        var myObject = { foo: "baz" };
        // later on in your application
        var boundFunction = dojo.hitch(myObject, myFunction);
        // the first value will be "bar", the second will be "baz";
        // the third will still be "bar".
        myFunction();       // "bar"
        boundFunction();    // "baz"
        myFunction();       // "bar"

可以看到,無論外部執行時上下文如何變化,dojo.hitch保證函式myFunction的上下文物件始終是myObject。

arguments物件

還記得之前Javascript被呼叫時建立上下文的步驟嗎?首先建立的就是arguments物件,arguments物件是一個函式的引數陣列,以引數的傳入順序排列該陣列內元素。

當函式定義完成後,其函式簽名就被固定下來。我們就不能增加或者刪除非匿名的函式引數。有時候這限制了函式呼叫的靈活性,我們不得不重寫新的函式來應付引數列表的變更,而dojo.partial方法打破了這個限制。

使用dojo.partial控制引數列表

首先看下面的例子(取自dojo/data模組),引數列表中有4個引數:
var putValue = function(store, item, attr, value){
    return store.setValue(item, attr, value);
}
然而後來在應用需要與putValue有相似的功能但引數列表不同的函式,如下所示:
//已宣告的myStore物件
var myStore = new dojo.data.ItemWriteStore({…});
someObject.setValueHandler = function(item, attr, value){
    myStore.setValue(item, attr, value);
};
dojo.partial是一個返回值為函式的函式,可以用預定義的值鎖定函式的前幾個引數。我們可以使用dojo.partial來避免someObject.setValueHandler的重複定義,程式碼如下:
var putValue = function(store, item, attr, value){
    return store.setValue(item, attr, value);
}
...
var myStore = new dojo.data.ItemWriteStore({...});
//dojo.partial返回了首個引數為myStore的putValue函式
someObject.setValueHandler = dojo.partial(putValue, myStore);
 
//等同於執行putValue(myStore, someItem, “foo”, “bar”)
someObject.setValueHandler(someItem, "foo", "bar");

這裡需要注意的是,dojo.partial並不鎖定函式的執行時上下文。

同時繫結執行時上下文和鎖定引數

如果我們希望同時繫結執行時上下文和鎖定引數,dojo.hitch本身就可以完成這兩個功能。dojo.hitch的頭兩個引數指的是要繫結的執行時上下文與對應函式,這兩個引數後面可接上任意多個作為第二個引數代表的對應函式的鎖定引數,程式碼如下:

someObject.setValueHandler = dojo.hitch(someObject, putValue, myStore);

// putValue的首個引數被鎖定為myStore,並且putValue中的this引數(如果有的話)指向someObject
someObject.setValueHandler(someItem, "foo", "bar");

總結

在本教程中,首先闡述了Javascript執行時上下文的基礎,然後介紹如何使用dojo.hitch方法固定函式的執行時上下文,又瞭解了使用固定值鎖定引數列表的方法-dojo.partial,最後介紹了使用dojo.hitch同時實現這兩個功能。

Dojo.hitch在事件驅動的程式設計模型中十分有效,使用者無需關心函式執行時上下文的變更,降低程式碼的耦合性。