摘要
- 三種方法均可改變函式
this
關鍵字的指向。apply()
接受一引數陣列,返回函式執行的結果。call()
接受一組引數,返回函式執行的結果。bind()
接受一組引數,返回函式體。需在bind()
後加小括號才能執行函式。- 箭頭函式的
this
繫結後無論使用apply()
、call()
還是bind()
都不可修改。
淺析this
關鍵字
- JavaScript中的函式存在
定義上下文
和執行上下文
,通過call()
、apply()
和bind()
可以改變this
的指向。this
總指向執行上下文。
定義上下文
定義上下文更準確的名稱應該叫詞法作用域,它指函式的定義部分所形成的作用域。
function fun1 () {
// 函式fun1的詞法作用域
// 函式定義
// ....
}
複製程式碼
執行上下文
函式在呼叫時會產生一個呼叫記錄,其中包含函式在哪裡被呼叫、傳入函式的引數等資訊。該記錄也被稱為執行上下文。一個函式的this
總指向函式的執行上下文。當fun1
在fun2
中呼叫時,fun1
的this
指向fun2
的定義上下文(詞法作用域)。
function fun2 () {
// fun1的this指向該作用域
fun1();
// 函式定義
// ...
}
複製程式碼
this
的指向
this
是執行上下文的一個屬性,因此常說this
指向函式的執行上下文。
this
是在函式呼叫時被繫結的,與函式的宣告位置(即詞法作用域)沒有任何關係。
call()
和apply()
call()
call()
方法的第一個引數為要指定的this
物件,第二個引數及以後為函式執行所需的引數列表。
let obj = {
a: 1
}
function fun1 (num1, num2) {
console.log(this); // obj {a: 1}
console.log(num1 + num2); // 3
console.log(this.a); // 1
}
fun1.call(obj, 1, 2);
複製程式碼
上述程式碼展示了call()
的兩種能力。一方面它改變了fun1
呼叫時的this
關鍵字,讓其指向obj,並通過this
關鍵字來訪問obj
中的屬性。另一方面它將自己接受到的引數傳入fun1
。
apply()
call()
和apply()
本質上並無太大差別,唯一的區別在於call()
接受的是引數列表,而apply()
接受的是一個引數陣列。
// ··· 同上
fun1.apply(obk, [1, 2]); // 效果和fun1.call(obj, 1, 2)相同
複製程式碼
bind()
bind()
建立一個新的函式, 當這個新函式被呼叫時this
鍵值為其提供的值,其引數列表前幾項值為建立時指定的引數序列。 ----- MDN
bind()
的使用方法和call()
十分類似,它的第一個引數是需要繫結的this
物件,之後的引數為函式執行所需的引數列表。下面讓我們來試驗一下。
let obj = {
a: 1
}
function fun1 (num1, num2) {
console.log(this);
console.log(num1 + num2);
console.log(this.a);
}
fun1.bind(obj, 1, 2); // fun1 { ··· }
複製程式碼
不同於apply()
和call()
,bind()
返回的並不是fun1
執行完畢的返回值,而是更改了this
並初始化引數之後的fun1
的函式定義。因此,要執行fun1
,需在bind()
後面再加一對括號:
fun1.bind(obj, 1, 2)(); // 改變this並傳入引數後執行fun1
複製程式碼
apply()
、call()
和bind()
的比較
相同點
- 均可改變函式
this
關鍵字的指向。
不同點
apply()
接受一引數陣列,返回函式執行的結果。call()
接受一組引數,返回函式執行的結果。bind()
接受一組引數,返回函式體。需在bind()
後加小括號才能執行函式。
箭頭函式的this
繫結
ES6中新加入了箭頭函式,它的繫結完全繼承自呼叫它的作用域。繫結後無論使用apply()
、call()
還是bind()
都不可修改。