JavaScript的call、apply

来源:互联网 发布:专业dj软件 编辑:程序博客网 时间:2024/06/06 18:32

call和apply是函数的方法。只要是函数都具有这两个方法。
在javascript OOP中,我们经常会这样定义:

 function cat(){ } cat.prototype={     food:"fish",          say: function(){alert("I love "+this.food);} } var blackCat = new cat(); blackCat.say(); 

但是如果我们有一个对象whiteDog = {food:”bone”},我们不想对它重新定义say方法,那么我们可以通过call或apply用blackCat的say方法:blackCat.say.call(whiteDog);
所以,可以看出call和apply是为了动态改变this而出现的,当一个object没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。
用的比较多的,通过document.getElementsByTagName选择的dom 节点是一种类似array的array。它不能应用Array下的push,pop等方法。我们可以通过:

var domNodes =  Array.prototype.slice.call(document.getElementsByTagName("*"));

所以call 和 apply 都是为了改变某个函数运行时的 context(即上下文)而存在的,换句话说,就是为了改变函数体内部 this 的指向。因为 JavaScript 的函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。
call和apply二者的作用完全一样,只是接受参数的方式不太一样,call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时,用 call,而不确定的时候,用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来便利所有的参数。
为了方便记忆:

猫吃鱼,狗吃肉,奥特曼打小怪兽。有天狗想吃鱼了猫.吃鱼.call(狗,鱼)狗就吃到鱼了猫成精了,想打怪兽奥特曼.打小怪兽.call(猫,小怪兽)

就这样记住了。
接下来再用一些例子来说明:

var a = {    user:"xxx",    fn:function(){        console.log(this.user);    }}a.fn(); //xxx

a对象的fn属性为一个函数,a.fn( )就是在执行函数,函数执行时,函数体中的this指向函数的调用者即a,打印a的user属性值,a的user属性值为xxx,所以打印xxx。
如果写成如下形式:

var a = {    user:"xxx",    fn:function(){        console.log(this.user);    }}var b = a.fn;b(); //undefined

这里b函数的调用者为window,所以函数中的this指向了window,而window中并没有user属性,所以执行b函数得到的是undefined。
利用已经知道的apply call的用法,可以将代码写成如下格式:

var a = {    user:"xxx",    fn:function(){        console.log(this.user);     }}var b = a.fn;b.call(a);//xxx

这里还是在执行b函数,只不过b函数中的this被指向了a对象,所以函数体中打印的就是a对象的user属性值,所以可以正常打印出xxx。
而如果将代码改写为如下格式:

var a = {    user:"xxx",    fn:function(){        console.log(this.user);     }}var b = a.fn;b.call(null);//undefined

用null替换了a,就相当于传入了window对象(非严格模式下),那么执行b函数的时候,this指向了window,window没有user属性,因此打印undefined。
接下来为函数加上一些参数:

var a = {    user:"xxx",    fn:function(arg1,arg2){        console.log(this.user);         console.log(arg1+arg2);     }}var b = a.fn;var args = [10,1];b.apply(a,args);//或b.apply(a,[10,1]) 或b.call(a,10,1)

以上执行的结果就是控制台上输出xxx,并输出11。
与call和apply相类似的还有一个bind方法。bind方法也可以用来改变this的指向,不同之处在于call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以返回一个被改变了this的函数,并且让这个改变后的函数按需调用。如果需要传入参数可以在调用bind方法时传入,也可以在函数调用时再传入。
代码示例:

var a = {    user:"xxx",    fn:function(){        console.log(this.user);    }}var b = a.fn;b.bind(a);

这段代码写完后控制台并不会输出xxx。这是因为b.bind(a)只会返回一个新的函数。即:

var a = {    user:"xxx",    fn:function(){        console.log(this.user);    }}var b = a.fn;var c = b.bind(a);c();

c为b.bind(a)返回的,已经改变了this指向的函数。因此虽然c函数的调用者是window对象,但是控制台上可以正常输出xxx而不是undefined。
如果有参数:

var a = {    user:"xxx",    fn:function(arg1,arg2){        console.log(this.user);        console.log(arg1+arg2);    }}var b = a.fn;var c = b.bind(a);c(10,1);

可以在c函数调用的时候,将需要的参数传入。当然,也可以写成这样:

var a = {    user:"xxx",    fn:function(arg1,arg2){        console.log(this.user);        console.log(arg1+arg2);    }}var b = a.fn;var c = b.bind(a,10,1);c();

或:

var a = {    user:"xxx",    fn:function(arg1,arg2){        console.log(this.user);        console.log(arg1+arg2);    }}var b = a.fn;var c = b.bind(a,10);c(1);

可见,传递参数的方式是比较灵活的。
本文参考资料:
来自追梦子的博客:JavaScript中call,apply,bind方法的总结
来自知乎的问答:如何理解和熟练运用js中的call及apply?

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微淘直播延迟怎么办 手机淘宝进群领金币怎么办 做淘客冲销量停止淘客后怎么办 微信中零钱提现怎么办 淘宝买家不签收怎么办 小龙虾没人下单怎么办 淘宝直播不浮现怎么办 淘宝直播看不了怎么办 理财客户说没钱怎么办 投资不给钱了怎么办 工作中遇到挫折怎么办 手机qq出现异常怎么办 农行卡出现异常怎么办 淘宝长期不发货怎么办 快递一直不发货怎么办 申请退款被拒绝怎么办 淘宝的垃圾短信怎么办 如果淘宝不退款怎么办 客服遇到客户骂怎么办 商场保证金不退怎么办 淘宝被投诉侵权怎么办 电脑wifi链接不见了怎么办 无线设备坏了怎么办 电脑网页默认了怎么办 淘宝商家停止服务怎么办? 淘宝商家停止了怎么办 做淘宝不会美工怎么办 做客服打字慢怎么办 淘宝客服学不会怎么办 代购不给退货怎么办 淘宝不能发照片怎么办 兼职刷手被骗怎么办 淘宝店商品被降权怎么办 淘宝宝贝被降权了怎么办 淘宝违规降权怎么办 淘宝商品降权怎么办 公司不想要了怎么办 派派被客服禁言怎么办 淘宝收款不发货怎么办 新品标签没了怎么办 商家签收不退款怎么办