call和apply的作用
重要定义: call和apply是为了动态的改变this而出现的(记住这句话),如果一个A对象没有某个方法fn,而另一B对象有,则可以调用A对象的call或apply,然后传入B对象,这样B对象就没必要定义新的fn方法了。
语法:
-
fun.call(thisArg[, arg1[, arg2[, ...]]])
- thisArg: fun函数运行时指定的this值。需要注意的是,this值并不一定是该函数执行时真正的this值;当在非严格模式下,如果该值为null或undefined,那么this指向的是全局对象(也就是浏览器中的window对象);如果该值为基本类型(数值、字符串、布尔值),那么this将指向基本类型值的自动包装对象
- arg1, arg2 .....: 参数列表
-
fun.apply(thisArg, [argArray])
- thisArg:与call方法的类似
-
[argArray]:一个数组或类数组对象。其中的数组元素将作为单独的参数传给fun函数(重要:参数元素将作为单独的参数传给fun函数);如果该值为null或undefined,则表示不需要传入任何参数。
Tips: 关于apply方法有一个很典型的用法就是使用它来求一个数组对象中的最大最小值(Math.max.apply(null|undefined, array) 和 Math.min.apply(null|undefined, array)),因为这两个方法不接受对象(其原型:Math.max([value1[, value2[, ...]]])),调用apply方法后,会将传入的数组对象中的数组元素作为单独的参数传入max和min方法中,这样就符合max和min的使用规则了
两者的不同点:使用call时,传入的是参数列表;而使用apply,传入的是数组或类数组对象;
示例1:使用call方法调用父构造函数
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
//这里调用Product, this表示的是Food,这样Food里也拥有了name和price属性
Product.call(this, name, price);
this.category = 'food';
}
function Toy(name, price) {
// 这里调用Product,this表示的是Toy,这样Toy里也拥有了name和price属性
Product.call(this, name, price);
this.category = 'toy';
}
var product = new Product('car', '200k');
var fruit = new Food('apple', '5');
var toy = new Toy('robot', '5k');
上面代码中,可以将Product看成一个父构造函数,Fruit和Food是Product下的两个子构造函数
示例2:使用call方法调用匿名函数
在这个例子中,创建了一个匿名函数,然后调用匿名函数的call方法,将数组元素作为指定的this值执行那个匿名函数(相当于匿名函数中this就指向了传入的数组元素),这个匿名函数的作用是给每个数组元素添加一个print方法。
var animal = [
{specis: 'lion', name: 'King'},
{specis: 'Whale', name: 'Fail'}
];
for (var i = 0; i < animal.length; i++) {
(function(i){
this.print = function(){
console.log('#' + i + ' ' + this.specis + ': ' + this.name);
};
this.print();
}).call(animal[i], i);
}
示例3:使用call方法调用函数并且执行上下文
当调用greet的call方法时,会将greet中的this绑定到i
function greet(){
var reply = [this.person, 'is an Awesome', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'Douglas Crockford',
role: 'Javascript developer'
};
greet.call(i);
示例4:使用apply和内置函数
var arr = [100, 10, 2, 20, 1];
console.log(Math.max.apply(null, arr));
注意事项
如下代码所示:
var arr = [1, 10, 20, 0, -1];
Math.math.apply(null, arr));
function convertArray() {
return Array.prototype.slice.apply(arguments);
}
为什么Math.math.apply()
中的第一个参数为null,而Array.prototype.slice.apply
中的第一个参数为需要处理的数据?
猜测:
因为Math.math()
方法的原型是:Math.max([value1[,value2[,...]]])
;也就是说它的参数是直接传入的,不需要去指定this
,因此调用apply时,this
指向null,实参放到第二个参数中;
而slice的原型是objArr.slice([begin[,end]]);
,也就是说slice()
是在数组对象上调用的,因此,必须要为其指定this
指向,也就是将arguments
放到第一个参数中;
总结:凡是涉及到调用对象的方法,都需要为其指定this