因為繼續研究Ajax Framework的原因,更多的接觸了call和apply。
故再次論述call和apply,
1)無(wú)疑關(guān)于call,最簡(jiǎn)單的解釋就是:把隱藏的第一個(gè)參數顯示化。因為通常一個(gè)函數(Function)的調用,會(huì )有一個(gè)額外的隱藏參數,就是函數(Function)所屬的對象(如果沒(méi)有所特指,則為global(如window)對象),在函數內你可用this關(guān)鍵字訪(fǎng)問(wèn)之。
從call的構造式 -- call(thisArg[,arg1,arg2…] ]);可看出
call(apply)方法可將一個(gè)函數的對象上下文(Function Context)從初始的上下文改變?yōu)橛?thisObj 指定的新對象,這就是利用call(apply)的最大優(yōu)勢。說(shuō)到此,我們不妨提提所謂的Function Context到底是為何物。先看看下面FunctionContextDemo:
解析上述代碼:
最初存在2個(gè)對象changed和original,changed即就是一個(gè)數組,分別有item和act屬性,而original除了具有和changed一樣的item和act屬性,還具有一個(gè)ask函數(詢(xún)問(wèn)到底是誰(shuí)坐在椅子上),故當調用original.ask()時(shí),可以看到意料中的結果:who's been sitting in my chair.而仔細往下看,當我們調用original.ask.call(changed)時(shí),你覺(jué)得會(huì )出現什么的結果呢?在此,是利用了call把original的方法(函數)ask給與changed對象來(lái)執行,原來(lái)changed是沒(méi)有ask方法(函數),如此綁定之后,函數的對象上下文(Function Context)即就是changed對象,故在方法(函數)ask里所調用的this就應該是changed對象,則可推知original.ask.call(changed)的結果應該是:who's been eating my banana.
通過(guò)FunctionContextdemo例子可看出如果我們需要在編程過(guò)程中需要替換函數的對象上下文(Function Context),call就是不錯的選擇。
你可以試著(zhù)把FunctionContextdemo例子修改如下,再看看是什么結果:
var changed={ item:"banana", act: "eating" };
var original={item: "chair",act: "sitting in"};
function ask(){
}
alert("Original : " + ask.call(original));
alert("changed: " + ask.call(changed));
2)javascript如何利用call來(lái)模擬面向對象中的繼承的,而且可以實(shí)現多重繼承
但仔細深入看看,這樣的繼承是有問(wèn)題的,直接在類(lèi)函數體里面定義成員方法,將導致每個(gè)實(shí)例都有副本,重復占用了內存。
最為優(yōu)雅簡(jiǎn)潔的方式應該算是基于原型(prototype)繼承。
3)接下來(lái)我們再次來(lái)看看javascript框架prototype里是如何利用apply來(lái)創(chuàng )建一個(gè)定義類(lèi)的模式:
var Class = {
};
解析:
從代碼看,該對象僅包含一個(gè)方法:Create,其返回一個(gè)函數,即類(lèi)。但這也同時(shí)是類(lèi)的構造函數,其中調用initialize,而這個(gè)方法是在類(lèi)創(chuàng )建時(shí)定義的初始化函數,如此就可以實(shí)現prototype中的類(lèi)創(chuàng )建模式.目的是規定如此一個(gè)類(lèi)創(chuàng )建模式,讓類(lèi)的初始化函數名一定是initialize(),而this.initialize.apply(this, arguments);(令人有些費解)則是保證initialize一定會(huì )在類(lèi)的實(shí)例創(chuàng )建后調用,既方便管理又體現Object-Oriented的思想。
注意: 里邊的this其實(shí)是同一對象,即相當于類(lèi)本身調用自己的構造函數來(lái)創(chuàng )建Class對象! 因為apply方法的第二個(gè)參數本身要求是一個(gè)數組,所以傳遞給該函數的參數也傳遞給類(lèi)的initialize方法,如果直接寫(xiě)為 this.initialize(arguments); 則所有的參數是作為一個(gè)數組傳遞給了initialize構造函數。
4) 活用apply(javascript框架prototype的事件綁定):
Function.prototype.bind = function() {
}
代碼解析:
該bind用途在于將某個(gè)函數綁定到特定的函數去執行,
a) var __method = this;這首先把Function Context重新賦值到一個(gè)本地變量,使得在Closure(這是一個(gè)javascript的特性,可解釋為"閉包")中都能訪(fǎng)問(wèn)到,如此在下邊就可以方便獲取了。它實(shí)質(zhì)就是bind方法的調用者, 是一個(gè)函數對象。
b) var args = $A(arguments);這里因為arguments本就是一個(gè)類(lèi)數組的對象,通過(guò)$A(arguments)將傳入到bind方法的參數都轉化為array.
c) var object = args.shift();通過(guò)截取args的第一個(gè)參數獲取Target Object(目標對象),此時(shí)args為除了第一個(gè)參數以外的參數構成的數組(array)
d) 這是最關(guān)鍵的一步,返回一個(gè)新的函數對象(不帶有任何的參數的函數),在此通過(guò)apply把__method(bind方法的調用者)綁定到Target Object(目標對象),并給與除了Target Object(目標對象)之外的所有參數構成的數組args.concat($A(arguments)),最終Target Object(目標對象)就可執行__method了。
如此費勁周折的綁定某一個(gè)函數所換來(lái)的優(yōu)勢是,從此你不需要顧及Function Context的混亂所帶來(lái)的額外顧慮。
聯(lián)系客服