復雜情況
讓我們來短暫地運行一下這個最后的例子。我們需要詢問deep_thought一個問題,如果不是直接運行click_handler而是通過點擊按鈕的話,那會發(fā)生什么事情?解決此問題的代碼貌似十分直接,我們可能會這樣做:
<script type="text/javascript"> function BigComputer(answer) { this.the_answer = answer; this.ask_question = function () { alert(this.the_answer); } }
function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById('thebutton');
the_button.onclick = deep_thought.ask_question; }
window.onload = addhandler; </script>
很完美吧?想象一下,我們點擊按鈕,deep_thought.ask_question被執(zhí)行,我們也得到了“42”。但是為什么瀏覽器卻給我們一個undefined? 我們錯在何處?
其實問題顯而易見:我們給ask_question傳遞一個引用,它作為一個事件處理函數(shù)來執(zhí)行,與作為對象方法來運行的上下文并不一樣。簡而言之,ask_question中的 this關鍵字指向了產(chǎn)生事件的DOM元素,而不是在BigComputer的對象中。DOM元素并不存在一個the_answer屬性,所以我們得到的是 undefined而不是”42″. setTimeout也有類似的行為,它在延遲函數(shù)執(zhí)行的同時跑到了一個全局的上下文中去了。
這個問題會在程序的所有角落時不時突然冒出,如果不細致地追蹤程序的每一個角落的話,還是一個非常難以排錯的問題,尤其在你的對象有跟DOM元素或者window對象同名屬性的時候。
使用.apply()和.call()掌控上下文
在點擊按鈕的時候,我們真正需要的是能夠咨詢deep_thought一個問題,更進一步說,我們真正需要的是,在應答事件和setTimeout的呼叫時,能夠在自身的本原上下文中呼叫對象的方法。有兩個鮮為人知的JavaScript方法,apply和call,在我們執(zhí)行函數(shù)呼叫時,可以曲線救國幫我們達到目的,允許我們手工覆蓋this的默認值。我們先來看call:
<script type="text/javascript"> var first_object = { num: 42 }; var second_object = { num: 24 };
function multiply(mult) { return this.num * mult; }
multiply.call(first_object, 5); // 返回 42 * 5 multiply.call(second_object, 5); // 返回 24 * 5 </script>
在這個例子中,我們首先定義了兩個對象,first_object和second_object,它們分別有自己的num屬性。然后定義了一個multiply函數(shù),它只接受一個參數(shù),并返回該參數(shù)與this所指對象的num屬性的乘積。如果我們呼叫函數(shù)自身,返回的答案極大可能是undefined,因為全局window對象并沒有一個num屬性除非有明確的指定。我們需要一些途徑來告訴multiply里面的this關鍵字應該引用什么。而multiply的call方法正是我們所需要的。
call的第一個參數(shù)定義了在業(yè)已執(zhí)行的函數(shù)內(nèi)this的所指對象。其余的參數(shù)則傳入業(yè)已執(zhí)行的函數(shù)內(nèi),如同函數(shù)的自身呼叫一般。所以,當執(zhí)行multiply.call(first_object, 5)時,multiply被呼叫,5傳入作為第一個參數(shù),而this關鍵字被設置為first_object的引用。同樣,當執(zhí)行multiply.call(second_object, 5)時,5傳入作為第一個參數(shù),而this關鍵字被設置為second_object的引用。
apply以call一樣的方式工作,但可以讓你把參數(shù)包裹進一個數(shù)組再傳遞給呼叫函數(shù),在程序性生成函數(shù)呼叫時尤為有用。使用apply重現(xiàn)上一段代碼,其實區(qū)別并不大:
<script type="text/javascript"> ...
multiply.apply(first_object, [5]); // 返回 42 * 5 multiply.apply(second_object, [5]); // 返回 24 * 5 </script>
apply和call本身都非常有用,并值得貯藏于你的工具箱內(nèi),但對于事件處理函數(shù)所改變的上下文問題,也只是送佛到西天的中途而已,剩下的還是得我們來解決。在搭建處理函數(shù)時,我們自然而然地認為,只需簡單地通過使用call來改變this的含義即可:
function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById('thebutton');
the_button.onclick = deep_thought.ask_question.call(deep_thought); }
代碼之所以有問題的理由很簡單:call立即執(zhí)行了函數(shù)(譯注:其實可以用一個匿名函數(shù)封裝,例如 the_button.onclick = function(){deep_thought.ask_question.call(deep_thought);} 但比起即將討論的bind來,依然不夠優(yōu)雅)。我們給onclcik處理函數(shù)一個函數(shù)執(zhí)行后的結(jié)果而非函數(shù)的引用。所以我們需要利用另一個JavaScript特色,以解決這個問題。
出處:Realazy
責任編輯:moby
上一頁 JavaScript 中的作用域 [2] 下一頁 JavaScript 中的作用域 [4]
◎進入論壇網(wǎng)頁制作、WEB標準化版塊參加討論,我還想發(fā)表評論。
|