JavaScript的call、apply

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?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值