在 JavaScript 中函式 (Function) 可以被直接呼叫,也可以做為物件中的方法 (method) 來使用。也因此 this
這個保留字所指向 (代表、表示) 的物件 (東西) 是會變動的,並非一個固定的值。從 ECMA 規範與語言實作[註]來看只是參考了當時的環境物件,所以從語法上來說當一個函式被使用時我們必須觀察傳入引數 (arguments) 的左邊,也就是小括號的左方是什麼。因為 this
只在乎是誰叫他工作的,所以嚴格來說,在嚴格模式下是不能夠直接以 fun();
這樣的方式去呼叫,必須像是調用物件方法般使用 window.fun();
這樣才可以。而這個時候我們就可以觀察函式左方就是環境物件。
另外由於函式 (Function) 並非基型值 (Primitive Value),所以變數名稱或是物件屬性在記憶體中會指向一個 Function 物件的位置,所以在將全域變數 objFun
指向 obj.fun
時 (objFun = obj.fun;
),實際上是將記憶體中的函式儲存位置指向給了全域變數,所以我們這樣透過 objFun();
呼叫函式的時候 this
才會指向全域環境物件 (非嚴格模式下的 window
或 global
)。
這些 JavaScript 語言的特性難以確保 this
被明確的指定出,若要明確的調用函式 (Function) 則可以使用 function
的「call」、「apply」、「bind」三個原型方法 (methods)。「call」可以指定函式執行時的環境 (Execution Context) 而間接的調用函式本身,this
會指向傳遞給 call(arguments)
的第一個引數。但 call(arguments)
的使用上需要把一個一個的引數傳入,若需要直接傳遞整組陣列作為引數,可以使用「apply」方法,它同樣的會將 this
指向第一個引數。而「bind」又有點不同了,雖然語法上跟 call
方法很類似,但實際並不是直接調用函式,而是回傳一個已經被指定 this
的全新函式。
在函式的使用上我們已經提到主要的三種:
– 作為函式 (functions)
– 作為物件方法 (methods)
– 作為物件建構式 (constructors)
– 間接調用 (call、apply、bind)
來講講建構式,每當我們使用 new
關鍵字調用函式的時候 JavaScript 編譯器 (引擎、語言實作) 會建立一個全新的物件並將這個物件指定為該函式的 this
,簡單來說 this
就是我們使用函式建出來的那個物件。
綜上所述的四個函式調用方式,我們可以更清楚的認識到 this
到底指到誰,簡單的判斷法則:
- 箭頭函式沒有自己的
this
,所以這個this
實際上是定義時所在的執行環境 (Execution Context)。 - 函式被使用時左邊是否有物件?
- 函式是不是被「call」、「apply」、「bind」調用的?
- 函式是不是被「new」調用的?
- 是在嚴格模式下嗎?
至此在沒有類別 (Class) 的 JavaScript 中我們也比較清楚的理解了 this
。而 class
這個語法糖式類導入後的狀況得等我比較瞭解之後再分享。若文中有錯請指證,感謝交流學習。