JavaScript中的call和apply的用途以及区别
apply接受两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数:
varfunc=function(a,b,c){ alert([a,b,c]);//输出[1,2,3] }; func.apply(null,[1,2,3]);
在这段代码中,参数1、2、3被放在数组中一起传入func函数,它们分别对应func参数列表中的a、b、c。
call传入的参数数量不固定,跟apply相同的是,第一个参数也是代表函数体内的this指向,从第二个参数开始往后,每个参数被依次传入函数:
varfunc=function(a,b,c){ alert([a,b,c]);//输出[1,2,3] }; func.call(null,1,2,3);
当调用一个函数时,JavaScript的解释器并不会计较形参和实参在数量、类型以及顺序上的区别,JavaScript的参数在内部就是用一个数组来表示的。从这个意义上说,apply比call的使用率更高,我们不必关心具体有多少参数被传入函数,只要用apply一股脑地推过去就可以了。call是包装在apply上面的一颗语法糖,如果我们明确地知道函数接受多少个参数,而且想一目了然地表达形参和实参的对应关系,那么也可以用call来传送参数。
call和apply的用途
1.改变this指向
call和apply最常见的用途是改变函数内部的this指向,我们来看个例子:
varobj1={ name:'sven' }; varobj2={ name:'anne' }; window.name='window'; vargetName=function(){ alert(this.name); }; getName();//输出:window getName.call(obj1);//输出:sven getName.call(obj2);//输出:anne
当执行getName.call(obj1)这句代码时,getName函数体内的this就指向obj1对象,所以此处的
vargetName=function(){ alert(this.name); };
实际上相当于:
vargetName=function(){ alert(obj1.name);//输出:sven };
在实际开发中,经常会遇到this指向被不经意改变的场景,比如有一个div节点,div节点的onclick事件中的this本来是指向这个div的:
document.getElementById('div1').onclick=function(){ alert(this.id);//输出:div1 };
假如该事件函数中有一个内部函数func,在事件内部调用func函数时,func函数体内的this就指向了window,而不是我们预期的div,见如下代码:
document.getElementById('div1').onclick=function(){ alert(this.id);//输出:div1 varfunc=function(){ alert(this.id);//输出:undefined } func(); };
这时候我们用call来修正func函数内的this,使其依然指向div:
document.getElementById('div1').onclick=function(){ varfunc=function(){ alert(this.id);//输出:div1 } func.call(this); };
2.Function.prototype.bind
大部分高级浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this指向,即使没有原生的Function.prototype.bind实现,我们来模拟一个也不是难事,代码如下:
Function.prototype.bind=function(context){ varself=this;//保存原函数 returnfunction(){//返回一个新的函数 returnself.apply(context,arguments);//执行新的函数的时候,会把之前传入的context //当作新函数体内的this } }; varobj={ name:'sven' }; varfunc=function(){ alert(this.name);//输出:sven }.bind(obj); func();
我们通过Function.prototype.bind来“包装”func函数,并且传入一个对象context当作参数,这个context对象就是我们想修正的this对象。
在Function.prototype.bind的内部实现中,我们先把func函数的引用保存起来,然后返回一个新的函数。当我们在将来执行func函数时,实际上先执行的是这个刚刚返回的新函数。在新函数内部,self.apply(context,arguments)这句代码才是执行原来的func函数,并且指定context对象为func函数体内的this。
这是一个简化版的Function.prototype.bind实现,通常我们还会把它实现得稍微复杂一点,
使得可以往func函数中预先填入一些参数:
Function.prototype.bind=function(){ varself=this,//保存原函数 context=[].shift.call(arguments),//需要绑定的this上下文 args=[].slice.call(arguments);//剩余的参数转成数组 returnfunction(){//返回一个新的函数 returnself.apply(context,[].concat.call(args,[].slice.call(arguments))); //执行新的函数的时候,会把之前传入的context当作新函数体内的this //并且组合两次分别传入的参数,作为新函数的参数 } }; varobj={ name:'sven' }; varfunc=function(a,b,c,d){ alert(this.name);//输出:sven alert([a,b,c,d])//输出:[1,2,3,4] }.bind(obj,1,2); func(3,4);
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!