目录
- 学习来源
- 过去的困惑
- 格式
- 相同之处
- 不同
- 小小知识点
1. 学习来源
推荐一篇教程,用例丰富、描述准确、总结到位:
深入浅出妙用 Javascript 中 apply、call、bind
上文作者的总结:
- 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);