瞟了一眼lodash的源碼,無意中看到這樣一個函數(shù):
function apply(func, thisArg, args) {
switch (args.length) {
case 0: return func.call(thisArg);
case 1: return func.call(thisArg, args[0]);
case 2: return func.call(thisArg, args[0], args[1]);
case 3: return func.call(thisArg, args[0], args[1], args[2]);
}
return func.apply(thisArg, args);
}
瞬間懵逼,不知道作者這樣寫的原因,往上瞧了瞧,作者給出這樣的注解:一個更快的,用來代替Function.prototype.apply的方法。
難道call比apply更快?
趕緊看了看ECMScriptg規(guī)范,才恍然大悟。
規(guī)范中指出了調(diào)用apply和call時發(fā)生的步驟。
下面,先講apply方法被調(diào)用,再講call方法被調(diào)用的場景。
func.apply(thisArg,argArray);
當(dāng)apply方法被調(diào)用時將執(zhí)行以下步驟:
步驟一:調(diào)用IsCallable
如果IsCallable(func) 返回false
,那么拋出一個TypeError的異常。因?yàn)檫@個func不能作為函數(shù)被調(diào)用。
如果IsCallable(func) 返回true
,則進(jìn)入第二步。
步驟二:判斷argArray是不是null
或undefined
,
如果是,那么就返回調(diào)用func內(nèi)部方法[[Call]]
的結(jié)果。調(diào)用內(nèi)部方法[[Call]]
時會提供thisArg——作為this的值和一個空參數(shù)列表。
如果argArray既不是null也不是undefined,那么就進(jìn)入第三步。
步驟三: 判斷argArray的類型是不是object
如果不是Object
類型,那么就拋出TypeError
異常。
如果是,就進(jìn)入第四步。
步驟四:定義變量len
len的值是調(diào)用argArray內(nèi)部方法[[Get]]
獲取argArray對象length
屬性值的結(jié)果,簡單的說,就是len = argArray[length];
步驟五: 定義變量n
n
的值是ToUint32(len)的結(jié)果
步驟六: 定義變量argList
,其值是一個空列表。
步驟七:定義index
變量,值為0
步驟八: 重復(fù)下面步驟,直到index < n
a. 定義變量indexName
,值為 ToString(index)的結(jié)果
b.定義變量nextArg
,值為用indexName
作為參數(shù),調(diào)用argArray內(nèi)部方法[[Get]]
的結(jié)果。簡單講就是 nextArg = argArray[indexName]。
c.將nextArg插入到argList
中,作為列表的最后一個元素。
d.將 index
+1
步驟九——最后一步: 返回結(jié)果
調(diào)用func
的內(nèi)部方法[[Call]]
并返回結(jié)果。提供兩個參數(shù),其中thisArg
作為this
的值,argList
作為函數(shù)的參數(shù)列表。
根據(jù)apply的執(zhí)行過程,我們也可以知道,apply函數(shù)的第二個參數(shù)如果是類數(shù)組,也能正常工作。
下面看看,call的執(zhí)行過程。它比apply的調(diào)用可簡單多了。
func.call (thisArg [ , arg1 [ , arg2, … ] ] )
當(dāng)call方法在func上被調(diào)用時(其中參數(shù)有thisArg和可選參數(shù)arg1,arg2...),會執(zhí)行以下步驟:
步驟一:調(diào)用IsCallable
如果IsCallable(func) 返回false
,那么拋出一個TypeError的異常。因?yàn)檫@個func不能作為函數(shù)被調(diào)用。
步驟二: 定義變量argList
,它是個空列表
步驟三: 如果call
調(diào)用時,有可選的參數(shù),那么從左往右把a(bǔ)rg1,arg2...加入到argList
的末尾。
步驟四——最后一步: 返回結(jié)果
調(diào)用func的內(nèi)部方法[[Call]]并返回結(jié)果。提供兩個參數(shù),其中thisArg作為this的值,argList作為函數(shù)的參數(shù)列表。
得出結(jié)論:
比較apply
與call
調(diào)用過程的不同,我們可以得出apply比call慢的原因主要是apply
方法中,對argArray
的參數(shù)有多次判斷,執(zhí)行步驟比call多。