这三个方法的作用基本上相同,用法上有一些不同,下面先对比一下它们的用法:
- apply:调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。
语法:
apply(thisArg)
apply(thisArg, argsArray)
- bind:创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
语法:
function.bind(thisArg[, arg1[, arg2[, ...]]])
- call:使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
语法:
function.call(thisArg, arg1, arg2, ...)
举个例子说明一下:
三个方法解决同样的问题:
JavaScript 新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。
代码:
apply:
<script>
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function (y) {
return this.x+':'+y;
},
};
var module02 = {
x: 91
};
console.log(module.getX(1));
var retrieveX = module.getX;
console.log(retrieveX(1));
// 返回 9:1 - 因为函数是在全局作用域中调用的
// 把 'this' 绑定到 module 对象
var boundGetX = retrieveX.apply(module,[1]);
console.log(boundGetX); // 81:1
// 把 'this' 绑定到 module02对象,module02继承了getX
var boundGetX2 = retrieveX.apply(module02,[1]);
console.log(boundGetX2); // 81:1
</script>
输出结果:
bind:
<script>
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function () {
return this.x;
},
};
console.log(module.getX());// 81
var retrieveX = module.getX;
console.log(retrieveX());
// 返回 9 - 因为函数是在全局作用域中调用的
// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81
</script>
输出结果:
call:
<script>
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function (y) {
return this.x+':'+y;
},
};
var module02 = {
x: 91
};
console.log(module.getX(1));
var retrieveX = module.getX;
console.log(retrieveX(1));
// 返回 9:1 - 因为函数是在全局作用域中调用的
// 把 'this' 绑定到 module 对象
var boundGetX = retrieveX.call(module,1);
console.log(boundGetX); // 81:1
// 把 'this' 绑定到 module02对象
var boundGetX2 = retrieveX.call(module02,1);
console.log(boundGetX2); // 81:1
</script>
输出结果:
从上面的例子可以看出,apply与call基本相同,区别是apply的第二个参数是传数组,而call是传一个或多个参数。bind的区别是调用时要加(),它返回的是一个函数。三者的作用都是为方法指定this的值。
用例:
1、apply用数组获取最大最小值
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
console.log(max);
// Expected output: 7
const min = Math.min.apply(null, numbers);
console.log(min);
// Expected output: 2
ES6 的写法:...
扩展运算符
Math.max(...[14, 3, 77])
2、将一个数组放到另外一个数组后面
// ES5 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
3、使用 call 方法调用匿名函数
在下例中的 for 循环体内,创建了一个匿名函数,然后通过调用该函数的 call 方法,将每个数组元素作为指定的 this 值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个 print 方法,这个 print 方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为 this 值传入那个匿名函数(普通参数就可以),目的是为了演示 call 的用法。
var animals = [
{ species: "Lion", name: "King" },
{ species: "Whale", name: "Fail" },
];
for (var i = 0; i < animals.length; i++) {
(function (i) {
this.print = function () {
console.log("#" + i + " " + this.species + ": " + this.name);
};
this.print();
}).call(animals[i], i);
}