深入淺出ES6(五):不定參數和默認參數

不定參數

我們通常使用可變參函數來構造API,可變參函數可接受任意數量的參數。例如,String.prototype.concat方法就可以接受任意數量的字符串參數。ES6提供了一種編寫可變參函數的新方式——不定參數。

我們通過一個簡單的可變參數函數containsAll給大家演示不定參數的用法。函數containsAll可以檢查一個字符串中是否包含若干個子串,例如:containsAll("banana", "b", "nan")返回true,containsAll("banana", "c", "nan")返回false。

首先使用傳統方法來實現這個函數:

function containsAll(haystack) {
  for (var i = 1; i < arguments.length; i++) {
    var needle = arguments[i];
    if (haystack.indexOf(needle) === -1) {
      return false;
    }
  }
  return true;
}

在這個實現中,我們用到了神奇的arguments對象,它是一個類數組對象,其中包含了傳遞給函數的所有參數。這段代碼實現了我們的需求,但它的可讀性卻不是最理想的。函數的參數列表中只有一個參數haystack,我們無法一眼就看出這個函數實際上接受了多個參數。另外,我們一定要注意,應該從1開始迭代,而不是從0開始,因爲arguments[0]相當於參數haystack。如果我們想要在haystack前後添加另一個參數,我們一定要記得更新循環體。不定參數恰好可以解決可讀性與參數索引的問題。下面是用ES6不定參數特性實現的containsAll函數:

function containsAll(haystack, ...needles) {
  for (var needle of needles) {
    if (haystack.indexOf(needle) === -1) {
      return false;
    }
  }
  return true;
}

這一版containsAll函數與前者有相同的行爲,但這一版中使用了一個特殊的...needles語法。我們來看一下調用containsAll("banana", "b", "nan")之後的函數調用過程,與之前一樣,傳遞進來的第一個參數"banana"賦值給參數haystack,needles前的省略號表明它是一個不定參數,所有傳遞進來的其它參數都被放到一個數組中,賦值給變量needles。對於我們的調用示例而言,needles被賦值爲["b", "nan"],後續的函數執行過程一如往常。(注意啦,我們已經使用過ES6中for-of循環。)

在所有函數參數中,只有最後一個纔可以被標記爲不定參數。函數被調用時,不定參數前的所有參數都正常填充,任何“額外的”參數都被放進一個數組中並賦值給不定參數。如果沒有額外的參數,不定參數就是一個空數組,它永遠不會是undefined。

默認參數

通常來說,函數調用者不需要傳遞所有可能存在的參數,沒有被傳遞的參數可由感知到的默認參數進行填充。JavaScript有嚴格的默認參數格式,未被傳值的參數默認爲undefined。ES6引入了一種新方式,可以指定任意參數的默認值。

下面是一個簡單的示例(反撇號表示模板字符串,上週已經討論過。):

function animalSentence(animals2="tigers", animals3="bears") {
    return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

默認參數的定義形式爲[param1[ = defaultValue1 ][, ..., paramN[ = defaultValueN ]]],對於每個參數而言,定義默認值時=後的部分是一個表達式,如果調用者沒有傳遞相應參數,將使用該表達式的值作爲參數默認值。相關示例如下:


animalSentence();                       // Lions and tigers and bears! Oh my!
animalSentence("elephants");            // Lions and elephants and bears! Oh my!
animalSentence("elephants", "whales");  // Lions and elephants and whales! Oh my!

默認參數有幾個微妙的細節需要注意:

  • 默認值表達式在函數調用時自左向右求值,這一點與Python不同。這也意味着,默認表達式可以使用該參數之前已經填充好的其它參數值。舉個例子,我們優化一下剛剛那個動物語句函數:

function animalSentenceFancy(animals2="tigers",
    animals3=(animals2 == "bears") ? "sealions" : "bears")
{
  return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

現在,animalSentenceFancy("bears")將返回“Lions and bears and sealions. Oh my!”。

  • 傳遞undefined值等效於不傳值,所以animalSentence(undefined, "unicorns")將返回“Lions and tigers and unicorns! Oh my!”。

  • 沒有默認值的參數隱式默認爲undefined,所以

function myFunc(a=42, b) {...}

是合法的,並且等效於

function myFunc(a=42, b=undefined) {...}

停止使用arguments

現在我們已經看到了arguments對象可被不定參數和默認參數完美代替,移除arguments後通常會使代碼更易於閱讀。除了破壞可讀性外,衆所周知,針對arguments對象對JavaScript虛擬機進行的優化會導致一些讓你頭疼不已的問題。

我們期待着不定參數和默認參數可以完全取代arguments,要實現這個目標,標準中增加了相應的限制:在使用不定參數或默認參數的函數中禁止使用arguments對象。曾經實現過arguments的引擎不會立即移除對它的支持,當然,現在更推薦使用不定參數和默認參數。

瀏覽器支持

Firefox早在第15版的時候就支持了不定參數和默認參數。

不幸的是,尚未有其它已發佈的瀏覽器支持不定參數和默認參數。V8引擎最近增添了針對不定參數的實驗性的支持,並且有一個開放狀態的V8 issue給實現默認參數使用,JSC同樣也有一個開放的issue來給不定參數默認參數使用。

BabelTraceur編譯器都支持默認參數,所以從現在起就可以開始使用。

發佈了99 篇原創文章 · 獲贊 56 · 訪問量 84萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章