天天看點

JS程式設計建議——60:比較函數調用模式

建議60:比較函數調用模式

在JavaScript中,函數就是對象,對象是“名/值”對的集合,并擁有一個到原型對象的隐藏連接配接。對象字面量産生的對象連接配接到object.prototype,函數對象連接配接到Function.prototype,該原型對象本身連接配接到object.prototype。每個函數在建立時都有兩個附加的隐藏屬性:函數的上下文和實作函數行為的代碼。

每個函數對象在建立時也随之帶一個prototype屬性,它的值是一個擁有constructor屬性且constructor屬性值為該函數的對象。這和隐藏連接配接到Function. prototype完全不同。這個令人費解的構造過程的意義将會在後面的章節中揭示。

作為對象,函數與其他值一樣可以在任何變量的位置使用。函數可以作為數組元素,可以作為函數的傳回值,可以作為對象的成員值,也可以作為表達式參與運算,同時函數也可以擁有自己的方法。

調用一個函數将暫停目前函數的執行,傳遞控制權和參數給新函數。除了聲明時定義的形式參數,每個函數接收兩個附加的參數:this和arguments。參數this在面向對象程式設計中非常重要,它的值取決于調用的模式。在JavaScript中一共有4種調用模式:方法調用模式、函數調用模式、構造器調用模式和apply調用模式。這些模式在如何初始化關鍵參數this上存在差異。

調用運算符是跟在任何産生一個函數值的表達式之後的一對圓括号,圓括号内可以包含零個或多個用逗号隔開的表達式。每個表達式産生一個參數值。每個參數值被賦予函數聲明時定義的形式參數名。當實際參數(arguments)的個數與形式參數(parameters)的個數不比對時不會導緻運作時錯誤。如果實際參數值過多,那麼超出的參數值将被忽略。如果實際參數值過少,那麼缺失的值将會被替換為undefined。不會對參數值進行類型檢查,任何類型的值都可以傳遞給參數。

(1)方法調用模式

當一個函數被儲存為對象的一個屬性時,将稱為一個方法。當一個方法被調用時,this被綁定到該對象。如果一個調用表達式包含一個屬性存取表達式(即一個點表達式或[subscript]下标表達式),那麼它被當做一個方法來調用。

var obj = {

}

obj.increment();

document.writeln(obj.value); // 1

obj.increment(2);

document.writeln(obj.value); // 3

在上面代碼中建立了obj對象,它有一個value屬性和一個increment方法。increment方法接受一個可選的參數,如果該參數不是數字,那麼預設使用數字1。

由于increment方法可以使用this去通路對象,是以它能從對象中取值或修改該對象。this到對象的綁定發生在調用的時候。這個延遲綁定使函數可以對this高度複用。通過this可取得increment方法所屬對象的上下文的方法稱為公共方法。

(2)函數調用模式

當一個函數并非一個對象的屬性時,它将被當做一個函數來調用:

var sum = add(3, 4); //7

當函數以此模式調用時,this被綁定到全局對象。這是語言設計上的一個錯誤。倘若語言設計正确,當内部函數被調用時,this應該仍綁定到外部函數的this變量。這個設計錯誤的後果是方法不能利用内部函數來幫助它工作,因為内部函數的this被綁定了錯誤的值,是以不能共享該方法對對象的通路權。幸運的是,有一個很容易的解決方案:如果該方法定義一個變量并将它指派為this,那麼内部函數就可以通過這個變量通路this。按照約定,将這個變量命名為that。

obj.doub();

document.writeln(obj.value); // 2

(3)構造器調用模式

JavaScript是一門基于原型繼承的語言,該語言是無類别的,對象可以直接從其他對象繼承屬性。當今大多數語言都是基于類的語言,雖然原型繼承有着強大的表現力,但它偏離了主流用法,并不被廣泛了解。JavaScript為了能夠相容基于類語言的編寫風格,提供了一套基于類似類語言的對象建構文法。

如果在一個函數前面加上new運算符來進行調用,那麼将建立一個隐藏連結到該函數的prototype原型對象的新執行個體對象,同時this将會被綁定到這個新執行個體對象上。注意,new字首也會改變return語句的行為。

var F = function(string) {

};

F.prototype.get = function() {

var f = new F("new object");

document.writeln(f.get()); //"new object"

上面代碼建立一個名為F的構速器函數,此函數建構了一個帶有status屬性的對象。然後,為F所有執行個體提供一個名為get的公共方法。最後,建立一個執行個體對象,并調用get方法,以讀取status屬性的值。

結合new字首調用的函數稱為構造器函數。按照約定,構造器函數應該儲存在以大寫字母命名的變量中。如果調用構造器函數時沒有在前面加上new,可能會發生非常糟糕的事情,既沒有編譯時警告,也沒有運作時警告,是以大寫約定非常重要。

(4)apply調用模式

JavaScript是函數式的面向對象程式設計語言,函數可以擁有方法。apply就是函數的一個基本方法,使用這個方法可以調用函數,并修改函數體内的this值。apply方法包括兩個參數:第一個參數設定綁定給this的值;第二個參數是包含函數參數的數組。例如:

var array = [5, 4];

var add = function() {

var sum = add.apply({}, array);

// 9

上面代碼建構一個包含兩個數字的數組,然後使用apply方法調用add()函數,将數組array中的元素值相加。

var status = F.prototype.get.apply(obj); //''obj''

上面代碼建構了一個構造函數F,為該函數定義了一個原型方法get,該方法能夠讀取目前對象的status屬性的值。然後定義一個obj對象,該對象包含一個status屬性,使用apply方法在obj對象上調用構造函數F的get方法,傳回obj對象的status屬性值。

繼續閱讀