昨天說到透過 this 關鍵字會在不同情境中取到不同的值,雖然會如何取值都是根據規範而來的,但實際上我們能手動改變 this 的指向,那就是透過函式原型的 apply()
、bind()
、call()
方法。透過這三者將使 this 不再根據原本的規則動態取值,而是永遠會拿到開發者所指定的物件。
fun.apply(thisArg, [argsArray])
thisArg
:要指定的 this 的值。
(非嚴格模式下,null 及 undefined 將會被全域物件取代,而原始型別將被包裝成物件)
argsArray
:一個 array-like 物件,這裡就是放原本函式需要的參數。
function greet(a0, a1) {
console.log(this.favorite, a0, a1);
}
const myObj = {
favorite: 'Apple'
}
// 正常呼叫函式會輸出「undefined 'Banana' 'Egg'」,其中 this 根據規則指向全域物件
greet('Banana', 'Egg');
// 透過 apply 呼叫函式會輸出「'Apple' 'Banana' 'Egg'」,其中 this 的值被指定為 myObj
greet.apply(myObj, ['Banana', 'Egg']);
fun.apply(thisArg, arg1, arg2, ...)
thisArg
:要指定的 this 的值。
(非嚴格模式下,null 及 undefined 將會被全域物件取代,而原始型別將被包裝成物件)
arg1, arg2, ...
:原本函式需要的參數。
(跟 apply 用法很像,只是沒有透過類陣列的形式包起來)
function greet(a0, a1) {
console.log(this.favorite, a0, a1);
}
const myObj = {
favorite: 'Apple'
}
// 正常呼叫函式會輸出「undefined 'Banana' 'Egg'」,其中 this 根據規則指向全域物件
greet('Banana', 'Egg');
// 透過 call 呼叫函式會輸出「'Apple' 'Banana' 'Egg'」,其中 this 的值被指定為 myObj
greet.call(myObj, 'Banana', 'Egg');
bind() 方法與前兩者不同,它會回傳一個 this 被綁定的新函式。該新函式被呼叫時, this 的值會是 bind 指定的參數,其他地方與原函式相同。
fun.bind(thisArg)
function greet(a0, a1) {
console.log(this.favorite, a0, a1);
}
const myObj = {
favorite: 'Apple'
}
// 正常呼叫函式會輸出「undefined 'Banana' 'Egg'」,其中 this 根據規則指向全域物件
greet('Banana', 'Egg');
// newGreet 是透過 bind 建立的新函式,它的 this 的值被指定為 myObj
const newGreet = greet.bind(myObj);
newGreet('Banana', 'Egg'); // 輸出「'Apple' 'Banana' 'Egg'」
這些函式原型的方法可以讓我們更加彈性的指定 this 的值,其中 apply
和 call
用法接近,而 bind
則是用再建立一個綁定 this 的函式。不過雖然它們很方便,不過在非不得已的情況下還是別太常使用比較好,畢竟這已經脫離了原本 this 的規則。尤其是透過 bind() 建立的新函式,呼叫方式跟原函式完全相同,容易發生在呼叫函式時沒注意到它的 this 的值已經被綁死,從而導致不可預期的錯誤。這是要多加注意的!