理解javascript中call(),aplay(),bind()方法

196 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

在js语言中,call(),aplay(),bind()方法都是Function这个对象所提供的方法,而这些方法的作用就是为了改变函数中this指向的。

在了解call(),aplay(),bind()之前,可能大家会有疑问,为什么要改变函数的this的指向?其实这也是call(),aplay(),bind()存在的原因,接下来,我们看下下面的代码

var obj = {
    name:"张三",
    age:"18",
    hobby:function(){
    
        console.log(this,this.name+"喜欢打篮球");
    }
}

obj.hobby(); //this指向obj,输出:张三喜欢打篮球

如上的一个对象,里面有啥三个属性,其中的hobby是一个函数,我们在里面打印了一句话,如果正常调用obj.hobby(),里面的this指向的就是obj这个对象,而这个对象的name值就是“张三”,最后结果如上。

那么,如果遇到下面的情况呢?

var obj = {
    name:"张三",
    age:"18",
    hobby:function(){
    
        console.log(this,this.name+"喜欢打篮球");
    }
}
//需求:特殊场景需要在1秒后执行hobby函数

setTimeout(obj.hobby,1000); //Window对象  喜欢打篮球

setTimeout了不了解都没关系,我们只是假设这种场景。而如果没有特殊指向,setTimeout中的this永远指向Window对象,它本来就是Window对象提供的方法。

在类似这样的场景下,就需要我们去手动改变this指向,这个时候,就需要用到call(),aplay(),bind()方法,看代码

var obj = {
    name:"张三",
    age:"18",
    hobby:function(){
    
        console.log(this,this.name+"喜欢打篮球");
    }
}
//需求:特殊场景需要在1秒后执行hobby函数

var hobby = obj.hobby;

setTimeout(function(){
   // hobby.call(obj);
   //hobby.apply(obj);
    hobby.bind(obj)();
},1000)

//以上是通过三种方式分别去做了处理,最后都会输出obj这个对象和张三喜欢打篮球

现在知道了,call(),aplay(),bind()确实是可以改变this指向的,然而既然有三个方法去做同一件事,那么一定会有区别:

(1)写法:

call()方法内接受参数若干,第一个参数为改变后的this指向,后面可以跟若干参数,用逗号隔;该方法调用后立即执行;

apply()方法接受两个参数,第一个参数为改变后的this指向,第二个参数为数组,将要传递的所有参 数必须放在数组内;该方法调用后立即执行;

bind()方法同call()方法,不同的是,bind方法并不会立刻执行,它会返回一个改变this执行后的方法(绑定函数),让我们之后调用。最关键的就是,因为它并非立即执行,因此,它可以在执行时再次传入参数,此时的参数会放在绑定时的参数后面一起传入原函数。

注:bind()为es6语法。

下面我们用代码加深一下关于这三种方法的理解:

代码片段一:求数组最大值,对比call和apply的用法


var arr =[33,22,44,88,55];
var maxNum = Math.max(arr);
var maxNumAp = Math.max.apply(Math,arr);
var maxNumCl = Math.max.call(Math,arr[0],arr[1],arr[2],arr[3],arr[4]);

console.log(maxNum,maxNumAp,maxNumCl)//NaN 88 88

上面代码输出结果一致,只是写法不一样。对于arr数组来说,它本身并没有max方法,但是Math有啊,就将this指向Math,则Math.max.apply(Math,arr) 成立。至于下面的,也不要抬杠多此一举,我们举例的本意就是为了直观看到区别

需求:请定义log方法模拟实现console.log功能

//apply写法
function log(){
    
    console.log.apply(console,arguments)
}

log(1,2,3)//1,2,3


//call写法
function log2(){
    console.log.call(console,...arguments)
}
log2(1,2,3)//1,2,3

第一次看到我感觉简直是脱裤子放屁,但是我们目的是为了学习call和apply。

还可以实现继承的效果哦


function f1(){
    this.name= "张三";
}

function f2(){
    f1.call(this);
   // f1.apply(this);
}

var f = new f2();

console.log(f.name);

关于call和apply大致就这样了,关于call和apply大家了解一下即可,基本上在自己的代码中会很少用到。类似上面的例子,其实是很无理的,而上面之所以那样写也是为了让大家直截了当的看清这两种方法的用法,希望大家不要误解,类似Math.max.apply(Math,arr),其实没必要的:Math.max(...arr)这样写性能会更好。实际开发中callapply更多的会用于做一些反柯里化的实现。

下面说一下bind()方法,还是文章开头的代码,我们做一些调整


var obj = {
    name:"张三",
    age:"18",
    hobby:function(){
               //此处用定时器模拟特殊情况,定时器内部我们需要用到obj的name,直接用this.name显然是不行的,所以,我们在定时器外定义一个变量来接收this
           
        var _this = this;
        
        setTimeout(function(){
        
            console.log(_this,_this.name+"喜欢打篮球");
            
        },1000)
        
     }
}
obj.hobby();

上面的代码我们可以用bind做得更优雅


var obj = {
    name:"张三",
    age:"18",
    hobby:function(){
        
        setTimeout((function(){
        
            console.log(this,this.name+"喜欢打篮球");
            
        }).bind(this),1000)
        
     }
}
obj.hobby();

这样的写法在jquery里面有很多。

总结:

(1)call,apply,bind方法都是为了改变this指向

(2)call,apply,bind方法第一个参数都是this的目标指向

(3)call,bind后面可以接收n个参数,逗号隔开,但是apply只能接受数组,如果有n个参数,必须放在数组中统一传递

(4)call和apply方法都是在调用后立即执行,bind则是调用后返回一个绑定函数,后续调用还可继续传参

在工作中本人应用的这三者也不多,保持学习的心态记录,如有误漏,欢迎指正