對于前端工程師來說,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就是返回一個數組的長度