call、apply、bind区别

72 阅读3分钟

我们都知道函数的this指向可以通过call、apply和bind进行修改,本文将对这三种方法的区别进行总结,以及对一些特殊值的this指向进行说明。

call方法

call方法,第一个参数指定this指向,后面传入一个参数列表作为函数的参数。
需要注意的是在某些情况下更改后的this可能不是第一个参数的实际值。
在非严格模式下,nullundefined将被替换为全局对象window,原始值将会被替换为对象。
    //定义一个对象,对象中的函数打印当前的this
    var obj = {
        hi:function(){
            console.log(this);
        }
    };
    //通过call改变this
    obj.hi.call(null)
    obj.hi.call(1)

非严格模式

image.png

可以看到在非严格模式下,将null作为call的第一个参数时,此时函数的this指向的是全局对象window,而将string、number、 boolean作为第一个参数传入时,this指向为其构造函数包裹的对象。

严格模式下

image.png

严格模式下,其this指向为参数本身

apply方法

apply方法,与call方法类似,不同之处在于,call是通过参数列表的形式将参数传给对应函数,apply则通过数组(或类数组)的形式传入。

既然call和apply的使用除了参数的形式不同之外,并没有其他不一样的地方,那么是否可以用call来代替apply?或者说为什么还要有apply的形式呢?

诚然,我们可以通过将数组的内容展开,在以call的形式修改其this指向,以此代替apply方法,但是前提是我们需要知道数组的长度,如果我们无法知道数组的长度,也就无法进行展开。但是在ES6出现之后,我们可以通过展开运算符...将任意数组进行展开,而不需要知道其具体长度,所以在ES6的时代,apply完全可以通过call取代。

bind方法

bind和call方法类似,第一个参数也是this的指向,之后也以参数列表的形式将参数传给对应函数。
不同之处在于,call需要一次性的将所有参数传入,而bind可以分多次传入,
并且this改变后不会立即执行,而是返回具有给定this值和初始参数的函数副本。
    //严格模式下
    var c = obj.hi.call(1);
    var b = obj.hi.bind(1);

image.png

通过查看运行结果,我们可以看到使用call改变this会立即执行函数,并且执行完毕后立即销毁该函数,所以当我们对 c进行类型监测是是undefined,这也就是为什么call必须一次性传入所有参数。

而调用bind方法,并没有立即执行,返回的类型为 function,所以我们才能在调用bind后继续为其传入参数。当我们调用 b 时,bind对函数修改的this值才会生效。

总结

call与apply的区别在于其传参形式的不同,call需要以参数列表形式传入,apply则需要传入一个类数组对象。 call、apply与bind的区别在于调用后的返回值,call、apply是函数的执行,而bind是函数的副本;call和apply需要一次性传入所有参数,bind则可多次传入参数。