1. 程式人生 > >ES6躬行記(2)——擴充套件運算子和剩餘引數

ES6躬行記(2)——擴充套件運算子和剩餘引數

  擴充套件運算子(Spread Operator)和剩餘引數(Rest Parameter)的寫法相同,都是在變數或字面量之前加三個點(...),並且只能用於包含Symbol.iterator屬性的可迭代物件(iterable)。雖然兩者之間有諸多類似,但它們的功能和應用場景卻完全不同。擴充套件運算子能把整體展開成個體,常用於函式呼叫、陣列或字串處理等;而剩餘引數正好相反,把個體合併成整體,常用於函式宣告、解構引數等。此處的整體可能是陣列、字串或類陣列物件等,個體可能是字元、陣列的元素或函式的引數等。

一、擴充套件運算子

  擴充套件運算子的用途簡單概括,可以分為以下三種。

(1)替代函式的apply()方法。

(2)簡化函式呼叫時傳遞實參的方式。

(3)處理陣列和字串。

1)apply()

  函式的apply()方法能夠間接呼叫其它物件的方法,往往能收穫奇效,例如用Math物件的min()方法獲取陣列中的最小值,min()方法本來只接收一組引數,利用apply()方法後就能直接傳遞一個數組,如下所示。

let arr = [1, 0, 2],
  min;
min = Math.min(1, 0, 2);                 //一組引數的呼叫方式
min = Math.min.apply(undefined, arr);    //利用apply()間接呼叫

  雖然apply()方法很便捷,但每次都必須設定this的指向(即定義第一個引數),並且迂迴的寫法可能會為理解程式碼意圖設定障礙。而使用擴充套件運算子後,既能以簡單的語法形式完成相同的功能,還能更清晰的表明程式碼的意圖。下面用擴充套件運算子查詢陣列中的最小值。

min = Math.min(...arr);
console.log(min);     //0

2)傳參

  函式在被呼叫時,實參通常都是以逗號分隔的序列形式傳遞到函式體內。如果實參的值被儲存在陣列中,那麼就要一個一個的讀取陣列中指定位置的元素,例如建立一個日期物件(呼叫它的建構函式),把年月日的資訊儲存在陣列中,如下程式碼所示。註釋中的日期並不是預設的顯示格式,只是為了更容易閱讀而這麼寫的。

let date = [2018, 6, 9];
new Date(date[0], date[1], date[2]);     //2018-7-6

  換成擴充套件運算子的寫法後,實參的傳遞就變得非常的簡潔,如下所示。

new Date(...date);                      //2018-7-6

  不僅如此,在呼叫函式的時候,還可以使用多個擴充套件運算子,並能和普通的實參混合使用,如下所示。

let time = [10, 28];
new Date(...date, ...time, 45);         //2018-7-6 10:28:45

3)陣列和字串

  在擴充套件運算子出現之前,要執行陣列的複製、合併等操作,需要呼叫陣列的slice()、concat()、unshift()等方法。這些方法到底是單獨呼叫還是組合呼叫,由實際情況而定。下面是一個數組複製與合併的簡單示例。

let arr1 = [1, 2, 3],
  arr2,
  arr3;
arr2 = arr1.slice();       //複製陣列
arr3 = arr2.concat(arr1);  //合併陣列
console.log(arr1);         //[1, 2, 3]
console.log(arr2);         //[1, 2, 3]
console.log(arr3);         //[1, 2, 3, 1, 2, 3]

  接下來用擴充套件運算子來完成同樣的功能,如下程式碼所示。

arr2 = [...arr1];              //複製陣列
arr3 = [...arr1, ...arr2];     //合併陣列

  在實際專案中,肯定會碰到各式各樣的陣列操作,合理利用擴充套件運算子,不但可以節省大量的程式碼,還能提升程式碼的可讀性。

  擴充套件運算子不僅能處理陣列,還能處理字串。在JavaScript中,字串的行為類似於陣列,但它不能直接呼叫陣列的方法,需要先執行自己的split()方法轉換成陣列。而使用擴充套件運算子後,就能省去這步操作,具體如下所示,注意,包裹的方括號不能省略。

let str = "strick";
str.split("");        //["s", "t", "r", "i", "c", "k"]
[...str];             //["s", "t", "r", "i", "c", "k"]

二、剩餘引數

  在JavaScript的函式中,宣告時定義的形參個數可以和傳入的實參個數不同。當實參個數大於形參個數時,ES6新增的剩餘引數能把沒有對應形參的實參收集到一個數組中。下面是一個簡單的示例。

function func(name, ...args) {
  console.log(name);
  console.log(args[0]);
}
func("strick");         //首先輸出"strick",然後輸出undefined
func("freedom", 29);    //首先輸出"freedom",然後輸出29

  第一次呼叫func()函式只傳入了一個實參,對應的形參就是name。第二次呼叫func()函式傳入了兩個實參,第一個有對應的形參,而第二個並沒有對應的形參。此時,該實參就會被放到陣列args(就是剩餘引數)中,變為該陣列的一個元素,在函式體內就能通過陣列的索引讀取該實參。有一點要注意,剩餘引數不會影響函式的length屬性,該屬性的值表示形參個數。以上面的func()函式為例,... args並不是一個形參,因此,func()函式的length屬性值為1。

console.log(func.length);         //1

1)解構

  剩餘引數可以被解構(將在第3篇中講解),這意味著剩餘引數中的元素可以被賦給函式體中的同名變數,如下所示。

function destructuring (name, ...[age]) {
  console.log(name);
  console.log(age);
}
destructuring ("jane", 28);        //首先輸出"jane",然後輸出28

  引入剩餘引數就是為了能替代函式內部的arguments,它是一個類陣列物件,管理著實參列表,該列表包含了傳入到函式內的所有實參。由於arguments物件不具備陣列的方法,所以很多時候在使用之前要先轉換成一個數組。而剩餘引數本來就是一個數組,避免了這多餘的一步,使用起來既優雅又自然。

2)兩點限制

  剩餘引數有兩點限制,在使用時需要引起注意。第一點是在函式中宣告時必須放在最後,下面是一種錯誤的寫法。

function restrict1(...args, name) {
  //丟擲語法錯誤
}

  第二點是不能在物件字面量的setter方法中宣告,因為該方法只接收一個引數,而剩餘引數不會限制引數的數量。注意,setter方法在定義時會用set替代function關鍵字,下面是一個會丟擲語法錯誤的例子。

var obj = {
  set age(...value) {
    this._age = value;
  }
};