深入解析 JavaScript this - 用 apply、call 和 bind 克服函式綁定挑戰
· 閱讀時間約 5 分鐘
在 JavaScript 中,this
的值取決於函式的呼叫上下文,而不是函式的定義位置。這使得 this 的行為有時候很難預測和控制。
首先我們先來複習一下 JavaScript 中 this
的行為:
this
在 JavaScript 中,this
是一個很特別的關鍵字,它的值會根據它所在的環境而改變。簡單來說,this
指的是呼叫函式時的上下文(context),也就是「誰」在呼叫這個函式。
傳統函式中的 this
在傳統函式裡,this
的指向取決於函式是**「怎麼」被呼叫的**
const person = {
name: "Alice",
greet: function () {
console.log(`Hi, I'm ${this.name}`);
},
};
// 這個 greet 函式是透過 person 物件來呼叫的。
// 這意味著,JavaScript 會把 this 綁定到 person,讓 this.name 指向 person.name。
person.greet(); // Hi, I'm Alice
const greetFunction = person.greet;
// 這裡的 this 就指向 window 物件,因為 greetFunction 是直接被呼叫的。
// 這時候 this.name 就會是 undefined 或 window.name 的值。
greetFunction(); // Hi, I'm
箭頭函式中的 this
箭頭函式的 this 行為可以理解為**「綁定」到箭頭函式定義時的詞法環境 (Lexical Environment)**,而不是函式被呼叫時的上下文。
const person = {
name: "Alice",
greet: () => {
console.log(`Hi, I'm ${this.name}`);
},
greet1: function () {
// 這裡的箭頭函式會繼承外層的 this,這個 this 是指向 person
const innerGreet = () => {
console.log(`Hi, I'm ${this.name}`); // 這裡的 this 指向 person
};
innerGreet();
},
};
// 這裡的 this 指向的是全域對象,而不是 person 物件
// person 物件並不是 greet 函式的外層上下文;this 的繼承來源是 greet 函式的定義上下文
person.greet(); // Hi, I'm
person.greet1(); // Hi, I'm Alice
this
的難題主要來自於它的綁定方式,這使得 this
的行為有時候很難預測和控制。
要解決 this
的問題,我們可以使用 Function.prototype.bind
、call
和 apply
方法
Function.prototype.bind
Function.prototype.bind
方法可以用來永久地綁定函式的 this
值,並回傳一個新的函式。
const person = {
name: "Alice",
greet: function () {
console.log(`Hello, ${this.name}`);
},
};
const greet = person.greet;
greet(); // 輸出 'Hello, undefined',因為 this 指向了全域物件
const boundGreet = person.greet.bind(person);
boundGreet(); // 輸出 'Hello, Alice',因為 this 被綁定到 person
Function.prototype.call 和 Function.prototype.apply
Function.prototype.call
和 Function.prototype.apply
方法可以用來臨時地綁定函式的 this
值,並立即執行這個函式。
call
和 apply
的差別在於傳入參數的方式,call
是逐個傳入,而 apply
是以陣列的方式傳入。
const person = {
name: "Alice",
};
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
greet.call(person, "Hello"); // 輸出 'Hello, Alice'
greet.apply(person, ["Hi"]); // 輸出 'Hi, Alice'
總結
bind()
:用於創建一個新的函式,這個函式的this
被固定為指定的物件。適用於需要保證this
的綁定時機的情境,例如回調函式。call()
和apply()
:用於立即呼叫函式並指定this
的值。call()
用逗號分隔的參數,apply()
用數組分隔的參數。- 箭頭函式:箭頭函式的
this
是從函式定義時的詞法環境繼承的,適合在函式內部使用時保證this
的一致性。
這些方法可以幫助你更好地控制和理解 this 的行為,使你的 JavaScript 程式碼更易於維護和預測。