bind、apply、call解析
阿新 • • 發佈:2019-01-29
目錄
- 學習來源
- 過去的困惑
- 格式
- 相同之處
- 不同
- 小小知識點
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);