Day 18:this 關鍵字 (4)
函數執行環境下 (Function Context) (續)
7. 回呼函數 (Callback Function) 裡的 this
this物件:視乎怎麼呼叫 Callback Function。
JavaScript 裡常會需要用到 Callback Function,將某個函數物件 A 當作參數傳進另一個函數 B,由函數 B 決定執行 A 的時機。
這時候就要注意函數 B 是如何去呼叫函數 A,否則函數 A —— 也就是 Callback Function —— 裡面的 this,很可能不是你所預期的對象。
7.1. 簡單呼叫 Callback Function (一般模式)
今天我有一個 hero 物件,存在一些屬性,例如 name,我想透過 Callback 的方式去控制 hero 物件,我只在 hero 物件裡實作一個 act(),負責執行 Callback Function。而 Callback Function 的內容由其他人提供。例如以下:
var name = "Hi I am Global";
function sayHi(){
return this.name;
}
var hero = {
name: "Hi I am a Hero",
act: function(cbk){
return cbk();
}
};
console.log( sayHi() ); // Hi I am Global
console.log( hero.act(sayHi) ); // Hi I am Globalhero.act(sayHi)就是一個 callback 的用法,sayHi就是 callback function。在
hero.act()裡,採用簡單呼叫的方式來執行 callback function。根據昨天介紹的「 2.簡易呼叫 (Simple Call) 」,函數裡的
this會是 Global 物件,所以hero.act(sayHi)回傳的結果是 Global 變數的name。
7.2. 簡單呼叫 Callback Function (嚴謹模式)
如果在嚴謹模式下,透過簡單呼叫,函數裡
this會是undefined,無法執行this.name,會發生錯誤。
但上述的範例,我想達到的效果是 hero.act(sayHi) 可以回傳物件 hero 自己的 name 值,我該怎麼做?
7.3. 用 apply() / call() 將物件本身傳入 Callback Function
如果想把物件本身帶入 Callback Function 裡的 this,就要用 apply() / call()。
總結 this
要判斷 this 是誰,就看是誰呼叫
this 出現的位置有可能在:
全域執行環境下 (Global Context)
函數執行環境下 (Function Context)
但全域執行環境下 (Global Context) 遇到的機率相對低,而且十分單純,this 就是 Global 物件本身。
比較容易發生問題的是函數執行環境 (Function Context),遇到的情境可能千變萬化,過程可能讓人很混淆。
記得一個大原則:看呼叫時的物件是誰。
JavaScript 的 this 不是看定義的語彙位置,而是根據執行當下誰擁有這段程式碼,也就是看誰呼叫的。
但要注意的是,簡單呼叫 (Simple Call) 的情形下,一般模式和嚴謹模式會有不同的行為。
函數執行環境下,判斷 this 的公式秘笈
所有人都知道,寫程式就像學數學一樣,不建議背誦,而是去理解背後原理 (話說回來,比樂透可能性還多的語法組合,企圖用背的也很不科學)。
但當你確定你已經理解原理之後,我不反對用一些類似口訣或公式筆記的方式來輔助記憶,幫助快速回憶。畢竟如果每次寫程式要用到時,都從頭推導,那效率是不現實的。
曾有人做過實驗,讓大學數學教授和高中生一起做同一份高中數學考券,結果高中生輕輕鬆鬆在時間內完成,數學教授卻沒有寫完。是因為數學教授的程度比高中生差嗎?
當然不是。
數學教授每一個題目都知道背後原理,給他足夠的時間推導,他每一題都能完美解答。但考試時間是有限的,對那些高中生來說,這些題目範圍是他們非常熟悉,幾乎看到題目腦海就浮現解法。這代表那些高中生不理解背後原理嗎?不,他們也是經過理解的過程學到這些題目的解法,但為了應付嚴峻的考試,他們用各種方法來加速遇到題目的解決速度,例如解題口訣、公式表。
現實中的專案開發也有同樣情境。專案開發通常有時程限制,為了減少每次回憶的時間,相信很多工程師都有自己的私藏筆記,這些筆記就是你理解的精華,用來幫助自己快速回想,或減少從頭推導的時間。
前面舉了非常多情境,整體歸納下來,大致上不出以下公式的範圍:
呼叫方式
模式
this 所指的物件
obj.method()
不限
該 obj 物件
function()
一般模式
Global 物件
function()
嚴謹模式
undefined
透過 apply() 或 call()
不限
第一個參數的物件 (若第一個參數是 null,則視同「function()」)
以上指的都是未經
bind()綁定而來的函數物件透過
bind()產生的函數,不管呼叫方式為何,this都指向當初bind()所綁定的物件。
小補充:this 不是變數
this 不是變數這一點我想大家都清楚,只是作為 this 介紹完整性的一個小補充。
正如最一開始所說,this 是一個關鍵字,不是變數,所以不能改變 this 的值,例如企圖這樣:
References
Last updated
Was this helpful?