今天我們來看 JavaScript 中的 this
綁定行為,分為兩種:一般 function 和 arrow function。不過在深入探討前,我們先從本質上來看,JS 的 this
與其他語言有什麼不同,以 C++ 為例:
C++ 這類的物件導向語言, this
固定指向函式被呼叫的物件實體,但 JS 的 this
指向由函式呼叫方式決定(動態決定),可以是全域物件、指定物件,甚至是 undefined
(嚴格模式下)。
嚴格模式(Strict mode)是 JavaScript 在 ES5 之後新增的一種語法特性,透過它可以讓 JS 以更嚴謹的方式執行,幫助開發者避免一些常見錯誤。
C++ 會在編譯時,自動把 this
帶入 member function,可以很直觀的理解成上述「this 永遠指向該物件本身」的實作;JS 則是執行時,依函式呼叫方式動態綁定。🫥⋯⋯什麼,實在是太抽象的話,我們看一個例子
person.greet()
中,對於 greet()
來說的 this
是 person
,因此可以正確的 console 出
Hi, I am Alice
但是,當呼叫 greetFn()
時,即便他被 assign 成 person.greet
,當他被呼叫的時候,他的 this
會被動態決定,有可能是一個全域物件,因此只會得到以下的結果(因為全域物件沒有 name )
Hi, I am
所以在使用 JS 的 this
時要特別注意綁定到的對象為誰,我們可以透過最一開始提到的 arrow function 來避免 refer 到預期外的 this
,主要原因是因為箭頭函式沒有自己的 this
,this
由外層作用域決定。
this
的範例雖然我們預期過一秒之後,obj.sayHi
會被呼叫,但這裡 setTimeout
拿到的是 obj.sayHi
的「引用」,沒有透過 obj
來呼叫,所以 this
丟了。
this
包起來setTimeout(() => obj.sayHi(), 1000); // this 保留在 obj 中,正確印出 Miya
這邊的 obj.sayHi()
還是透過 obj
呼叫的,因此 this
是 obj
,沒問題!
另一個例子是即便我們使用 class,仍然可能會丟失 this
,同樣的可以使用 arrow function callback 來保留 class 裡的 this
。
bind
綁定 this
bind
會回傳一個新的函式,將 this
綁定到你指定的物件。這邊的例子中 sayHi
裡的 this
透過 bind
綁定到obj
上,this.name
就可以正確印出 "Miya"
。
setTimeout(obj.sayHi.bind(obj), 1000);
接著,我們來看 React 裡頭如何使用 class fields + arrow function 解決 this
丟失問題
handleClick
是以箭頭函式定義的 class field,會自動綁定 this
到當前的 class instance。因此,handleClick
傳給 <button>
的 onClick
時,裡面的 this
不會丟失,也不需要在 constructor 裡手動綁定。
不過現在的主流寫法(≥ React 18)已經不再寫 class component 而是 functional component,事件處理不需要手動綁定 this
,只要使用箭頭函數就能用外層作用域,這個情況下根本沒有 this
的問題!