这三个方法的作用基本上相同,用法上有一些不同,下面先对比一下它们的用法:
- 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继承了getXvar 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());// 81var 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: 7const 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);
}