call和apply方法
call、apply和bind的作用
call和apply都是用来改变函数中的this关键字的:
- 首先,让函数执行。
- 然后,再把当前函数中的this改变为call和apply中的第一个参数传递的值。
bind的作用: 仅仅改变函数中的this,不执行函数。
三者的区别
1、call和apply的区别:
执行的语法:
函数名.call(obj, arg1, arg2, ...) 函数名.apply(obj, [arg1, arg2, ...])
它们的区别在于:只有在传递参数值的时候,call是一个个传值,apply是传递一个数组。但是不管怎样,都是相当于给每个形参分别赋值。
var obj = {
name: "AAA"
};
function sum(n1, n2) {
console.log(this);
}
sum.call(obj); //=>1.执行sum; 2.让sum中的this变为obj
sum.apply(obj);
sum.call(obj, 12, 13);
sum.apply(obj, [12, 13]);
2、bind和其他两个方法的区别:
(1)bind方法返回的是一个修改过后的函数;call和apply没有返回值。
(2)bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。
(3)call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加。
var a = {
user:"aaa",
fn:function(e,d,f){
console.log(this.user); //=>aaa
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);
call和apply的细节知识点
- 在JS非严格模式下,用call和apply改变函数中的this关键字,如果没有传递任何参数,或者第一个参数是
null/undefined
,this默认都变为window了。 - 在JS严格模式下,不传递或传递undefined,
this-->undefined
,如果传递null,则this-->null
。
注:call和apply中的第一个参数永远是用来改变函数中的this关键字的,所以如果传递数字12,function sum(n1, n2) { console.log(this); } sum.call(12,13); //=>[Number: 12] sum.apply([12,13]); //=>[ 12, 13 ]
this=new Number(12)
。
应用
- 使用call将类数组arguments变成数组。
ES6中的扩展运算符(function sum() { console.log(Array.prototype.slice.call(arguments)); //=>[ 12, 13 ] console.log([].slice.call(arguments)); //=>[ 12, 13 ] } sum(12,13);
...
)也能实现。function sum() { console.log([...arguments]); } sum(12,13);
- 获取数组最大/最小值。
使用Math.max()方法获取最大值,参数为一系列数字。
不能传数组作为参数,所以使用apply方法传参。console.log(Math.max(12, 13, 14)); console.log(Math.min(12, 13, 14));
除了上述方法之外。还可以用ES6的扩展运算符(var arr = [1,2,3]; console.log(Math.max.apply(null, arr)); //不需要改this,所以传null作为第一个参数
...
)实现。var arr = [1,2,3]; console.log(Math.max(...arr));
- call和原型结合。
call和apply是定义在Function.prototype
上的方法,call的原理如下:Function.prototype.call = function (para1) { //1.让当前操作的函数执行,this->当前操作的函数 this(); //2.让当前操作函数中的this变为第一个参数值 //this(当前要操作的函数)中的this(函数中的this)变为para1 //this.this = para1; }
练习题:
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
console.log(this);
}
fn2.call(fn1); //=>2,fn1
/*下面这行代码的执行步骤:首先让fn2.call执行,然后让里面的this变为fn1。这里需要注意:
fn2.call === fn2.__proto__.call === Function.prototype.call
所以fn2.call执行相当于Function.prototype.call执行,并让Function.prototype.call中的this变为fn1,所以相当于执行fn1。
值得注意的是,当call大于两个的时候,都只是执行fn1而fn2其实不会执行。
这是因为不管写多少个call,都只是去Function.prototype上找call,并拿出来执行,
执行call的时候其实是执行里面的this()方法,而该方法来源于call的第一个参数。
*/
fn2.call.call(fn1); //=>1
Function.prototype.call(fn1); //执行Function.prototype,没有输出。
Function.prototype.call.call(fn1); //=>1