1. 程式人生 > >bind、apply、call解析

bind、apply、call解析

目錄
  1. 學習來源
  2. 過去的困惑
  3. 格式
  4. 相同之處
  5. 不同
  6. 小小知識點

1. 學習來源

上文作者的總結

  • apply 、 call 、bind 三者都是用來改變函式的this物件的指向的;
  • apply 、 call 、bind 三者第一個引數都是this要指向的物件,也就是想指定的上下文;
  • apply 、 call 、bind 三者都可以利用後續引數傳參;
  • bind 是返回對應函式,便於稍後呼叫;apply 、call 則是立即呼叫 。

2. 過去的困惑

過去對於js的學習就停留於“傳參直接呼叫方法”,認為Array.prototype.push.apply這樣的語句有點抽象,無法理解。

// 用例目的:把 array2 追加到 array1 的尾部
var array1 = [1,2,3,4,5]; 
var array2 = [7,7,7,7,7]; 

Array.prototype.push.apply(array1, array2);            /* array1 值為  [1,2,3,4,5,7,7,7,7,7] */
// 實際就是以array1這一陣列變數呼叫了Array物件的push方法
// 並且將array2中的每一個元素拆解為一個push方法的引數

具體擴充套件後:

Array.prototype.push.apply(array1,  array2);

等同於如下寫法:

array1.push(array2[0],  array2[1],  array2[2],  array2[3],  array2[4]);

es6優化後:

// 使用es6的"...擴充套件運算子"取代apply方法
array1.push(...array2) ;

追加一個小栗子:

// ES5 的寫法
Math.max.apply(null, [14, 3, 77])

// ES6 的寫法
Math.max(...[14, 3, 77])

// 等同於
Math.max(14, 3, 77);

3. 格式

var func = function (arg1, arg2) {};

func.apply(this, [arg1, arg2]);       // 呼叫func函式,並且指定this為當前作用域,並且把兩個引數以陣列的形式傳入

func.call(this, arg1, arg2);            // 呼叫func函式,並且指定this為當前作用域,順序傳入引數 

func.bind(this);                            // 為func函式繫結作用域為“當前的this”,留作後續呼叫

4. 相同之處

bing、apply、call三者都:

  • 用於改變函式指向的this物件,即函式作用域;
  • 傳入的第一個引數即為this指向的新物件,即“想要指定的上下文”
  • 都可以傳入引數(bind的傳參類似於傳入預設值)

5. 不同

bind: 建立並返回一個新函式,並繫結每一次的this,但並不呼叫
apply:指定此次的this,並且立即呼叫;(以陣列形式傳參)
call:指定此次的this,並且立即呼叫;(多引數逐個傳參)

6. 小小知識點

6.1 多次呼叫bind是無效的

var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func();          // 輸出3,繫結的上下文是foo
 
var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func();         // 輸出3,繫結的上下文依然是foo

文首作者解釋的無效原因

bind() 的實現,相當於使用函式在內部包了一個 call / apply ,第二次 bind() 相當於再包住第一次 bind() ,故第二次以後的 bind 是無法生效的。

對於這段話,我的理解是:
對於bind呼叫鏈來說,執行順序是從後往前的。
var func = bar.bind(foo).bind(sed).bind(fiv);
這一句的實際呼叫順序是bind(fiv) ==> bind(sed) ==> bind(foo);
也可以看做 ( ( (bar.bind(fiv)) ).bind(sed) ).bind(foo);