apply
前端面试中,很多面试官会问你,apply是做什么的?apply和call的区别?
你可以告诉面试官,apply和call只有一个区别,就是传入的参数格式不同,apply传入一个数组参数 [argsArray],而call传入参数列表 arg1, arg2, …
1.Apply的语法结构
有2个参数
thisArg:在 fun 函数运行时指定的 this 值,非严格模式下,也就是没有写”use strict”,通常这个值设置为null或者undefined,则指向全局对象(大部分是时候是window)。
argsArray:数组对象或者类数组对象,类数组对象只支持ES5,你可以不传任何参数,则为null 或 undefined。fun.apply(thisArg, [argsArray])
.
2.使用apply和内置函数:内置函数有很多,比如Array实现的内置函数、Math实现的内置函数
使用apply调用Array的内置函数push(),可以写成下面这样。1
2
3
4
5
6
7
8
9
10var arr1 = [1, 3, 4]
var arr2 = [2, 4, 5]
//第一个参数表示正在调用调用push()的对象,这行代码的意思是说把arr2的参数传入arr1,你可以看成是arr1.push(arr2)这种行为,但不是等价的。
Array.prototype.push.apply(arr1, arr2) //[1, 3, 4, 2, 4, 5]
//类似下面这种写法,他们的输出是一样的。
arr2.forEach(function(value) {
arr1.push(value)
}) // [1, 3, 4, 2, 4, 5]
var num = [6, 3, 10, 2, 8];
Math.max.apply(null, num); // null说明当前函数执行的上下文是在全局作用域,输出10
总结:apply作为内置函数调用传参是最经常使用的场景,唯一的难点在于指定第一个参数的运行环境,毕竟一个this可以难倒一大批前端工程师。
call
使用call方法调用匿名函数1
2
3
4
5
6
7
8
9
10
11
12
13
14var people = [
{name: '阿里妈妈', age: 18},
{name: '阿里爸爸', age: 22}
];
//一开始你可能会这样写:
for (var i = 0; i < people.length; i++) {
(function (i) {
setTimeout(function () {
console.log(this.name + ': ' + this.age);
}, i * 1000)
})(i);
} //输出
//"JS Bin Output : undefined"
//"JS Bin Output : undefined"
然后发现this指向不对,你可能会想到给函数设置一个变量指向当前的this,但是这样也不对,因为输出的this.name和this.age是指向数组animals的对象,那么这时候你需要给匿名函数用到call来绑定this指向的是animals里面循环的对象。然而你会发现输出还是undefined,为什么呢?
原因在于匿名函数里面的this是指向了people[i]的对象,但是setTimeout里面的this是指向window的,所以你需要用到bind()绑定this。1
2
3
4
5
6
7
8
9
10for (var i = 0; i < people.length; i++) {
(function (i) {
//这里也可以用var _this = this来改变this的上下文(指向)
setTimeout(function () {
console.log(this.name + ': ' + this.age);
}.bind(this), i * 1000)
}).call(people[i], i);
} //输出
//"阿里妈妈: 18"
//"阿里爸爸: 22"