JavaScript內(nèi)幕——call比apply快得多

瞟了一眼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是不是nullundefined,

如果是,那么就返回調(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é)論:

比較applycall調(diào)用過程的不同,我們可以得出apply比call慢的原因主要是apply方法中,對argArray的參數(shù)有多次判斷,執(zhí)行步驟比call多。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • ??引用類型的值(對象)是引用類型的一個實(shí)例。 ??在 ECMAscript 中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)...
    霜天曉閱讀 1,088評論 0 1
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,882評論 0 38
  • 函數(shù)和對象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念。通過函數(shù)可以封裝任意多條語句,而且...
    道無虛閱讀 4,630評論 0 5
  • 1月5日精進(jìn):敬畏—進(jìn)入—體驗(yàn)—交給—持續(xù) 1,缺啥補(bǔ)啥,怕啥練啥; 2,一切為我所用,所用為團(tuán)隊(duì)家; 3,我想...
    京心達(dá)畢玉娜閱讀 128評論 0 0
  • 今日加班,無暇寫字,以舊詩補(bǔ)。季夏首日,也算應(yīng)景,準(zhǔn)備歸巢。 一重紗簾一重明半句蛙聲半句蟲臥榻好眠夢不擾長此可作壽...
    半都閱讀 839評論 9 14