當 JavaScript 沒有 Class 時 this 會指向哪裡?

Photo by Andrew Neel on Unsplash

在 JavaScript 中函式 (Function) 可以被直接呼叫,也可以做為物件中的方法 (method) 來使用。也因此 this 這個保留字所指向 (代表、表示) 的物件 (東西) 是會變動的,並非一個固定的值。從 ECMA 規範與語言實作[註]來看只是參考了當時的環境物件,所以從語法上來說當一個函式被使用時我們必須觀察傳入引數 (arguments) 的左邊,也就是小括號的左方是什麼。因為 this 只在乎是誰叫他工作的,所以嚴格來說,在嚴格模式下是不能夠直接以 fun(); 這樣的方式去呼叫,必須像是調用物件方法般使用 window.fun(); 這樣才可以。而這個時候我們就可以觀察函式左方就是環境物件。

另外由於函式 (Function) 並非基型值 (Primitive Value),所以變數名稱或是物件屬性在記憶體中會指向一個 Function 物件的位置,所以在將全域變數 objFun 指向 obj.fun 時 (objFun = obj.fun;),實際上是將記憶體中的函式儲存位置指向給了全域變數,所以我們這樣透過 objFun(); 呼叫函式的時候 this 才會指向全域環境物件 (非嚴格模式下的 windowglobal)。

這些 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 到底指到誰,簡單的判斷法則:

  1. 箭頭函式沒有自己的 this,所以這個 this 實際上是定義時所在的執行環境 (Execution Context)。
  2. 函式被使用時左邊是否有物件?
  3. 函式是不是被「call」、「apply」、「bind」調用的?
  4. 函式是不是被「new」調用的?
  5. 是在嚴格模式下嗎?

至此在沒有類別 (Class) 的 JavaScript 中我們也比較清楚的理解了 this。而 class 這個語法糖式類導入後的狀況得等我比較瞭解之後再分享。若文中有錯請指證,感謝交流學習。