在 JavaScript 中,改變函數內部 this 的指向常見的方式有 call()
、apply()
、bind()
,而這三種方式又存在些微差異。
call()
call()
方法會呼叫(執行)一個函數
,且可以同時改變函數內部的 this
指向。
語法:
1 | fun.call(thisArg ,arg1, arg2, ...); |
參數說明:
- thisArg:在函式運行時的
this
指向,若省略則僅執行函式而不改變this
指向。 - arg1, arg2, …:傳遞的參數(可省略)。
例如:
在非嚴格模式下,函式 foo 中的 this
指向的原本是 window
,因在此例子中,foo
其實是屬於 window
物件的一個方法。(若在嚴格模式下,則為 undefined
)
1 | var obj = { |
剛提到,call()
會呼叫(執行)函式,但若無給第一個參數,則僅會呼叫函式而不會改變 this
指向,所以除了用 foo()
執行 foo
函式之外,也可以透過 call()
來呼叫(當然不需要多此一舉)。
1 | foo.call(); // window |
現在除了呼叫 foo 函式,同時想要將函式 foo 裡的 this 指向改為 obj
,可以使用 call()
來達成。
1 | foo.call(obj); // obj |
還可以傳遞一些參數。
1 | var obj = { |
實現繼承
call()
也可以用來實現繼承。
1 | function Person(name, age){ |
apply()
使用 apply()
會呼叫一個函數,同時可以改變函數內部 this
指向,apply()
與 call()
幾乎一樣,最大的不同是 call()
接受一連串的參數,而 apply()
接受一組陣列(或類陣列)形式的參數。
語法:
1 | fun.apply(thisArg, [argsArray]); |
參數說明:
- thisArg:在函式運行時的
this
指向。 - [argsArray]:傳遞的參數必須為陣列或類陣列(可省略)。
bind()
bind()
能改變 this
指向,但不會呼叫
(執行)函數,會 copy 並返回一個新的函數。
語法:
1 | fun.bind(thisArg, arg1, arg2, ...) |
參數說明:
- thisArg:在 fun 函數運行時指定的
this
值。 - arg1, arg2, …:傳遞其他參數(可省略)。
例如:
使用 bind()
將函式 foo 裡的 this
指向改為 obj
,同時傳遞 1
和 2
兩個引數,有別於 call()
與 apply()
,bind()
並不會呼叫執行 foo
函式,僅會返回一個新的函式。
1 | var obj = { |
因 bind()
並不會呼叫執行 foo 函式,而是返回一個新的函式,若想要同時執行 foo
,則需手動加上 ()。
1 | foo.bind(obj, 1, 2)(); |
現在來看看返回的新函式長什麼樣子,可以打開瀏覽器,並輸入以下代碼。
1 | var obj = { |
印出 newFoo
:
咦? newFoo
不就是 foo
嗎?
再試著輸入以下代碼,事實證明 newFoo
不等於 foo
。
1 | console.log(newFoo === foo); // false |
在開發中,最常使用的應該是 bind()
,因為很多情況下我們只是想改變 this
,不想同時執行函數。
例如現在有一個按鈕,想要在用戶點擊後,禁用此按鈕 3 秒。
1 | <button>按鈕</button> |
若直接在定時器裡使用匿名 function(){}
形式的 callback,定時器裡的 this
指向的是 window
。
1 | const btn = document.querySelector('button'); |
這時候 bind()
就可以派上用場了,因為不想要在改變 this
的同時執行 this.disabled = false
,而是希望透過定時器 3 秒後執行,此時就不適用 call()
與 apply()
。
1 | const btn = document.querySelector('button'); |
當然,也可以直接將定時器裡的 callback function 改成箭頭函式(arrow function)
的形式來解決。
1 | const btn = document.querySelector('button'); |
總結
call()
、apply()
、bind()
皆可以改變函數內部的 this
,三者的差異在於:
自動呼叫函數 | 參數(可省略) | |
---|---|---|
call() | ⭕️ | arg1, arg2, … |
apply() | ⭕️ | [arg1, arg2, …] |
bind() | ❌ | arg1, arg2, … |
參考資料: