一、常規
在 JavaScript 中,apply、call、bind 是三個與函數呼叫相關的方法,它們都允許你在呼叫函數時手動設定函數的上下文(即 this 指向)。
1、apply 方法:apply 方法允許你呼叫一個函數,並且手動設定函數的上下文(即 this 指向)以及傳遞一個引數陣列。其語法如下:
function.apply(thisArg, [argsArray])
其中,thisArg 表示函數要繫結的上下文,argsArray 是一個可選的陣列,其中包含將傳遞給函數的參數列。
例如,以下程式碼會將陣列 [1, 2, 3] 作為引數傳遞給函數 foo,並且將函數的上下文設定為物件 obj:
function foo(a, b, c) {
console.log(a + b + c);
}
var obj = {
x: 1,
y: 2,
z: 3
};
foo.apply(obj, [1, 2, 3]); // 輸出 6
2、call 方法:call 方法與 apply 方法類似,也是允許你呼叫一個函數,並且手動設定函數的上下文(即 this 指向),但是它需要你手動傳遞一個參數列,而不是一個引數陣列。其語法如下:
function.call(thisArg, arg1, arg2, ...)
其中,thisArg 表示函數要繫結的上下文,arg1、arg2、... 是將傳遞給函數的參數列。
例如,以下程式碼會將引數 1、2、3 傳遞給函數 foo,並且將函數的上下文設定為物件 obj:
function foo(a, b, c) {
console.log(a + b + c);
}
var obj = {
x: 1,
y: 2,
z: 3
};
foo.call(obj, 1, 2, 3); // 輸出 6
3、bind 方法:bind 方法與 apply、call 方法不同,它並不會立即呼叫函數,而是會返回一個新的函數,並且這個新函數的上下文(即 this 指向)被永久地繫結到了指定的物件上。其語法如下:
function.bind(thisArg, arg1, arg2, ...)
其中,thisArg 表示函數要繫結的上下文,arg1、arg2、... 是一些可選的引數,這些引數將在呼叫新函數時作為參數列傳遞給原函數。
例如,以下程式碼會將函數 foo 的上下文繫結到物件 obj 上,並返回一個新函數 newFoo,當呼叫 newFoo 時,它的引數將被傳遞給原函數 foo:
function foo(a, b, c) {
console.log(a + b + c + this.x + this.y + this.z);
}
var obj = {
x: 1,
y: 2,
z: 3
};
var newFoo = foo.bind(obj, 1, 2, 3);
newFoo(); // 輸出 12
下面是一個範例,演示了使用 apply、call、bind 方法的一些特性:
function foo(a, b) {
console.log(this.x + this.y + a + b);
}
var obj1 = {
x: 1,
y: 2
};
var obj2 = {
x: 3,
y: 4
};
// 使用 apply 方法呼叫函數 foo,並將 obj1 作為上下文
foo.apply(obj1, [3, 4]); // 輸出 10
// 使用 call 方法呼叫函數 foo,並將 obj2 作為上下文
foo.call(obj2, 5, 6); // 輸出 18
// 使用 bind 方法系結函數 foo 的上下文為 obj1,並建立一個新函數 newFoo
var newFoo = foo.bind(obj1, 7);
newFoo(8); // 輸出 18
// 使用 bind 方法系結函數 foo 的上下文為 obj2,並建立一個新函數 newFoo2
var newFoo2 = foo.bind(obj2);
newFoo2(9, 10); // 輸出 26
二、特殊
1、如果在使用 apply、call、bind 方法時沒有傳遞 thisArg 引數或者傳遞了 null 或 undefined,那麼預設的上下文將是全域性物件(在瀏覽器環境中,通常是 window 物件)。
function foo() {
console.log(this === window); // 在瀏覽器環境中,輸出 true
}
// 使用 apply 方法呼叫函數 foo,沒有傳遞 thisArg 引數
foo.apply(); // 輸出 true
// 使用 call 方法呼叫函數 foo,傳遞了 null 作為 thisArg 引數
foo.call(null); // 輸出 true
// 使用 bind 方法系結函數 foo 的上下文為 null,並建立一個新函數 newFoo
var newFoo = foo.bind(null);
newFoo(); // 輸出 true
2、如果使用 bind 方法系結了函數的上下文後,再使用 apply 或 call 方法呼叫這個函數,那麼繫結的上下文將會被忽略,仍然使用傳遞給 bind 方法的上下文。
var obj1 = {
x: 1,
y: 2
};
var obj2 = {
x: 3,
y: 4
};
function foo() {
console.log(this.x + this.y);
}
// 使用 bind 方法系結函數 foo 的上下文為 obj1,並建立一個新函數 newFoo
var newFoo = foo.bind(obj1);
// 使用 apply 方法呼叫新函數 newFoo,並將 obj2 作為上下文
newFoo.apply(obj2); // 輸出 3
// 使用 call 方法呼叫新函數 newFoo,並將 obj2 作為上下文
newFoo.call(obj2); // 輸出 3
在這個範例中,先使用 bind 方法將函數 foo 的上下文繫結為 obj1,並建立了一個新函數 newFoo。然後,分別使用 apply 和 call 方法呼叫新函數 newFoo,並將 obj2 作為上下文。由於使用了 bind
方法系結了上下文,無論傳遞了什麼引數,都不會改變繫結的上下文。這就是繫結的上下文被忽略的情況。最終輸出的結果為 3,而不是 7。
3、如果使用 bind 方法系結了函數的上下文後,再使用 new 操作符建立範例,那麼繫結的上下文將被忽略,而是建立一個新的物件作為 this,並且原函數中的 this 將會指向這個新物件。
function Foo() {
this.x = 1;
this.y = 2;
}
var obj = {
x: 3,
y: 4
};
// 使用 bind 方法系結函數 Foo 的上下文為 obj,並建立一個新函數 NewFoo
var NewFoo = Foo.bind(obj);
// 使用 new 操作符建立範例,此時會忽略繫結的上下文 obj,而是建立一個新物件作為 this
var newObj = new NewFoo();
console.log(newObj.x); // 輸出 1
console.log(newObj.y); // 輸出 2
console.log(obj.x); // 輸出 3
console.log(obj.y); // 輸出 4
在這個範例中,先使用 bind 方法將函數 Foo 的上下文繫結為 obj,並建立了一個新函數 NewFoo。然後,使用 new 操作符建立範例,此時會忽略繫結的上下文 obj,而是建立一個新物件作為 this。因此,範例 newObj 中繼承了繫結函數 Foo 中定義的屬性 x 和 y,輸出 1 和 2。同時,原函數中的 this 指向了新建立的物件 newObj,因此在原函數中定義的屬性 x 和 y 也被新增到了新物件上。
綜上所述,apply、call、bind 方法都是用於手動設定函數的上下文(即 this 指向),在實際開發中也經常被使用。對於這三個方法,需要了解它們的使用方法、語法和一些注意事項,以便在開發中更加靈活地使用它們。