JS call、apply、bind的使用與區別

2022-03-15 19:00:31
在 JavaScript 中,每個函數都包含兩個非繼承而來的函數 apply() 和 call(),這兩個函數的作用是一樣的,都是為了改變函數執行時的上下文而存在的,實際就是改變函數體內 this 的指向。

而 bind() 函數也可以達到這個目的,但是在處理方式上與 call() 函數和 apply() 函數有一定的區別,接下來我們就詳細看下 3 者的使用方式。

call()函數的基本使用

call() 函數呼叫一個函數時,會將該函數的執行物件上下文改變為另一個物件。其語法如下所示:

function.call(thisArg, arg1, arg2, ...)

  • function 為需要呼叫的函數。
  • thisArg 表示的是新的物件上下文,函數中的 this 將指向 thisArg,如果 thisArg 為 null 或者 undefined,則 this 會指向全域性物件。
  • arg1,arg2,...表示的是函數所接收的參數列。

我們可以通過下面的範例看看 call() 函數的用法:

// 定義一個add()函數
function add(x, y) {
    return x + y;
}
// 通過call()函數進行add()函數的呼叫
function myAddCall(x, y) {
    // 呼叫add()函數的call()函數
    return add.call(this, x, y);
}
console.log(myAddCall(10, 20));    //輸出“30”

myAddCall() 函數自身是不具備運算能力的,但是我們在 myAddCall() 函數中,通過呼叫 add() 函數的 call() 函數,並傳入 this 值,將執行 add() 函數的主體改變為myAddCall() 函數自身,然後傳入引數 x 和 y,這就使得 myAddCall() 函數擁有 add() 函數計算求和的能力。在實際計算時,就為 10 + 20 = 30。

apply()函數的基本使用

apply() 函數的作用域與 call() 函數是一致的,只是在傳遞引數的形式上存在差別。其語法格式如下:

function.apply(thisArg, [argsArray])

  • function 與 thisArg 引數與 call() 函數中的解釋一樣。
  • [argsArray] 表示的是引數會通過陣列的形式進行傳遞,如果 argsArray 不是一個有效的陣列或者 arguments 物件,則會丟擲一個 TypeError 異常。

要想實現與 call() 函數中一樣的範例效果,可以使用 apply() 函數編寫以下程式碼:
//定義一個add()函數
function add(x, y) {
    return x + y;
}
// 通過apply()函數進行add()函數的呼叫
function myAddApply(x, y) {
    // 呼叫add()函數的apply()函數
    return add.apply(this, [x, y]);
}
console.log(myAddApply(10, 20));    //輸出“30”
與 call() 函數相比,apply() 函數只需要將 add() 函數接收的引數使用陣列的形式傳遞即可,即使用[x, y]的形式,執行後的結果為 10 + 20 = 30。

bind()函數的基本使用

bind() 函數建立一個新的函數,在呼叫時設定 this 關鍵字為提供的值,在執行新函數時,將給定的參數列作為原函數的引數序列,從前往後匹配。其語法格式如下:

function.bind(thisArg, arg1, arg2, ...)

事實上,bind() 函數與 call() 函數接收的引數是一樣的,其返回值是原函數的副本,並擁有指定的 this 值和初始引數。

如果我們想要實現上面範例的效果,可以編寫以下程式碼:
//定義一個add()函數
function add(x, y) {
    return x + y;
}
// 通過bind()函數進行add()函數的呼叫
function myAddBind(x, y) {
    // 通過bind()函數得到一個新的函數
    var bindAddFn = add.bind(this, x, y);
    // 執行新的函數
    return bindAddFn();
}
console.log(myAddBind(10, 20));    //輸出“30”

call()函數、apply()函數、bind()函數的比較

三者的相同之處是:都會改變函數呼叫的執行主體,修改 this 的指向。

不同之處表現在以下兩點:

  • 第一點是關於函數立即執行,call() 函數與 apply() 函數在執行後會立即呼叫前面的函數,而 bind() 函數不會立即呼叫,它會返回一個新的函數,可以在任何時候進行呼叫。
  • 第二點是關於引數傳遞,call() 函數與 bind() 函數接收的引數相同,第一個參數列示將要改變的函數執行主體,即 this 的指向,從第二個引數開始到最後一個參數列示的是函數接收的引數;而對於 apply() 函數,第一個引數與 call() 函數、bind() 函數相同,第二個引數是一個陣列,表示的是接收的所有引數,如果第二個引數不是一個有效的陣列或者 arguments 物件,則會丟擲一個 TypeError 異常。