apply接收两个参数:
- 指定了函数体内this对象的指向;
- 一个带下标的集合(可以是数组或类数组)
var func = function(a, b, c){
console.log([a, b, c]); // 输出[1, 2, 3]
}
func.apply(null, [1, 2, 3]);
call接收一个或多个参数:
- 指定了函数体内this对象的指向;
- 从第二个参数往后,每个参数被依次传入函数;
var func = function(a, b, c){
console.log([a, b, c]); // 输出[1, 2, 3]
}
func.call(null, 1, 2, 3);
从上面的两个例子我们可以看出apply和call的唯一的区别就是:传入参数的数量是否固定。通常call在以下情况使用:
- 函数传入的参数数量确定;
- 需要清晰地表达形参和实参之间的对应关系
apply和call使用注意点:
如果传入的第一个参数是null,this就会指向全局对象,在浏览器上面是window,在nodejs上是global;
- 在1的条件下,如果使用严格模式,函数体内的this还是为null;
var func = function(a, b, c){ "use strict"; console.log(this === null); // 输出true } func.apply(null, [1, 2, 3]);
apply和call的用途:
-
借用其他对象的方法:
// 例一 Math.max.apply(null, [1, 2, 5, 3, 4]); // 输出5 // 例二 var A = function(name){ this.name = name; }; var B = function(){ A.apply(this, arguments); }; B.prototype.getName = function(){ return this.name; }; var b = new B('sven'); console.log(b.getName()); // 输出:'sven'
-
改变this的指向:
var obj1 = { name: 'sven', }; var obj2 = { name: 'anne', }; global.name = 'global'; var getName = function(){ console.log(this.name); } getName(); // 输出:global getName.call(obj1); // 输出:sven getName.call(obj2); // 输出: anne
-
Function.prototype.bind的模拟实现:
// 简单实现 Function.prototype.bind = function(context){ var self = this; return function(){ return self.apply(context, arguments); } } var obj = { name: 'sven', } var func = function(){ console.log(this.name); // 输出:sevn }.bind(obj); func(); // 复杂实现 Function.prototype.bind = function(){ var self = this, context = [].shift.call(arguments), args = [].slice.call(arguments); return function(){ return self.apply(context, [].concat.call(args, [].slice.call(arguments))); } } var obj = { name: 'sven', }; var func = function(a, b, c, d){ console.log(this.name); // 输出:sven console.log([a, b, c, d]); // 输出:[1, 2, 3, 4] }.bind(obj, 1, 2); func(3, 4);
本篇博客参考自JavaScript设计模式与开发实践,[p29-p33]