对于前端工程师来说,callapply算是常用的函数方法,允许通过函数和在函数调用中指定this的指向。那么这两个方法到底有什么区别呢?本文将详细介绍这两个方法,顺便加深对其理解。

call

方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。允许为不同的对象分配和调用属于一个对象的函数/方法。提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

const obj = {
    site:"80sz.com"
}
function func(firstName, lastName) {
    console.log(firstName + ' ' + this.site + ' ' + lastName);
}
func.call(obj, 'Tang', 'Quintion');   //Tang 80sz.com Quintion

语法

function.call(thisArg, arg1, arg2, ...)
  • thisArg可选的。
    在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
  • arg1, arg2, ...
    指定的参数列表。

返回值

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

apply

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

语法

func.apply(thisArg, [argsArray])
  • thisArg必选的。
    在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
  • argsArray可选的。
    一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或  undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。

返回值

调用有指定this值和参数的函数的结果。

call和apply的区别

它们的作用一模一样,区别仅在于传入参数形式的不同。

apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传递给调用的函数:

const func = function(a,b,c){
    console.log([a,b,c]);
}
func.apply(null,[1,2,3]);   // [ 1, 2, 3 ]

这段代码中,参数 1、2、3 被放在数组中一起传入 func 函数,它们分别对应 func 参数列表中的 a 、b 、c。

call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数:

const func = function(a,b,c){
    console.log([a,b,c]);
}
func.call(null,1,2,3);   // [ 1, 2, 3 ]

调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的区别,JavaScript 的参数在内部就是用一个数组来表示的。

从这个意义上说,apply 比 call 的使用率更高,不关心具体多少参数被传入函数,只要用 apply 一股脑地推过去就可以了。

call 是包装在 apply 上的一颗语法糖,如果明确地知道函数接收多少个参数,而且想一目了然地表达形参和实参的对应关系,那么可以用 call 来传送参数。

当使用 call 或者 apply 的时候,如果传入的第一个参数为 null ,函数体内的 this 会指向默认的宿主对象,在浏览器中则是 window :

const func = function(a,b,c){
    console.log(this === window );
}
func.apply(null,[1,2,3]);   // true

如果是在严格模式下,函数体内的 this 还是为 null :

const func = function(a,b,c){
    console.log(this === null );
}
func.apply(null,[1,2,3]);   // true

改变this指向

const obj = {
    site:"80sz.com"
}
function func() {
    console.log(this.site);
}
func.call(obj);  // 80sz.com

call 方法的第一个参数是作为函数上下文的对象,上面的代码把 obj 作为参数传给了 func,此时函数里的 this 便指向了 obj 对象。此处 func 函数相当于下面的方法:

function func() {
    console.log(obj.site);
}

apply()方法的一些妙用

Math.max 可以实现得到数组中最大的一项

因为Math.max不支持Math.max([param1,param2])也就是数组,但是它支持Math.max(param1,param2...),所以可以根据apply的特点来解决 var max=Math.max.apply(null,array),这样就轻易的可以得到一个数组中的最大项(apply会将一个数组转换为一个参数接一个参数的方式传递给方法)这块在调用的时候第一个参数给了null,这是因为没有对象去调用这个方法,我只需要用这个方法帮我运算,得到返回的结果就行,所以直接传递了一个null过去。

用这种方法也可以实现得到数组中的最小项:Math.min.apply(null,array)

Array.prototype.push实现两个数组的合并

同样push方法没有提供push一个数组,但是它提供了push(param1,param2...paramN),同样也可以用apply来转换一下这个数组,即:

var arr1=new Array("1","2","3");
var arr2=new Array("4","5","6");
Array.prototype.push.apply(arr1,arr2);    //得到合并后数组的长度,因为push就是返回一个数组的长度