天天看點

Rest參數與Spread操作符

Rest參數(剩餘參數)…:

在 JavaScript 中,很多内建函數都支援傳入任意個參數。

例如:

  • Math.max(arg1, arg2, …, argN) —— 傳回入參中的最大值。
  • Object.assign(dest, src1, …, srcN) —— 依次合并 src1…N 的屬性到 dest。
  • 等等

在本章中我們會學會如何編寫實作上述功能的代碼,更重要的是,我們要學會如何得心應手地處理及使用這些函數和數組。

在JavaScript中,無論函數定義了多少個形參,你都可以傳入任意個實參進行調用。

如下:

function sum(a, b) {
  return a + b;
}

alert( sum(1, 2, 3, 4, 5) );      

雖然這裡不會傳入多餘的參數而報錯,但是多餘的參數也不會起任何作用,函數隻會傳回前倆個參數相加的結果。

針對上例,我們可以在定義函數時使用Rest參數,Rest參數的操作符表示為3個點 ​…​ ,直白地講,它的意思就是“把剩餘的參數都放到一個數組中”。

舉個例子,我們需要把所有的參數都放到數組args中:

function sumAll(...args) {  //數組變量名為args
  let sum = 0;
  for (let arg of args) sum += arg;
  return sum;
}
alert(sumAll(1)); //1
alert(sumAll(1, 2));  //3
alert(sumAll(1, 2, 3));   //6      

我們也可以顯式地定義和取用前面部分的參數,是以下面這種用法是行不通的:

function f(arg1, ...rest, arg2) { // ...rest後面還有個arg2?
  //error
}      

…rest必須是最後一個參數哦

​"arguments"變量​

函數的上下文會提供一個非常特殊的類數組對象arguments,所有的參數被按序放置。

例如:

function showName() {
  function showName() {
    alert( arguments.length );
    alert( arguments[0] );
    alert( arguments[1] );
    // 它是可周遊的
    // for(let arg of arguments) alert(arg);
  }
// 依次彈出提示:2,Julius,Caesar
showName("Julius", "Caesar");
// 依次彈出提示:1,Ilya,undefined(不存在第二個參數)
showName("Ilya");
}      

在 JavaScript 引入 Rest 參數之前,無論入參數是多是少,想擷取所有的入參隻能使用 arguments。

時至今日,這仍是一個可用的方法。

即使 arguments 是一個類數組且可周遊的變量,但它終究不是數組。它沒有數組原型鍊上的函數,我們沒法直接調用諸如 arguments.map(…) 等這樣的函數。

同樣的,因為它總是包含所有的參數,我們并不能像使用 Rest 參數一樣,期望它隻截取入參的一部分。

是以如果你不想受困于以上“缺點”,那麼趕緊使用 Rest 參數吧。

​注意:箭頭函數是沒有“arguments”的​

如果我們在箭頭函數中通路 arguments,此時的 arguments 并不屬于箭頭函數,而是屬于箭頭函數外部的“普通”函數。

請看下例:

function f() {
   let showArg = () => alert(arguments[0]);
   showArg();
}
f(1);    //1      

我們已經知道箭頭函數自身是沒有 this 的,現在我們更進一步還知道它缺少 arguments 這個特殊的對象。

Spread操作符(展開操作符)

我們已經學會了如何把一系列的參數收集到數組中。

不過有時候我們也需要做與之相反的事情。

比如,内建函數 Math.max 會傳回參數中最大的值:

alert( Math.max(3, 5, 1) ); // 5      

假如我們已有數組 [3, 5, 1],我們該如何用它調用 Math.max 呢?

直接把數組“原樣”傳入是不會奏效的,因為 Math.max 期待你傳入一系列的數值型參數,而不是單一的數組:

let arr = [3, 5, 1];

alert( Math.max(arr) ); // NaN      

毫無疑問我們不可能手動地去一一設定參數 Math.max(arg[0], arg[1], arg[2]),因為我們不确定需要設定多少個參數。待代碼最終執行時,這個參數數組可能很大,也可能啥也沒用。這樣手動設定實為下策。

Spread 操作符 來拯救你了!它看起來和 Rest 參數操作符很像,都表示為 …,但是二者完全做了相反的事。

當在函數調用時使用 …arr,它會把可疊代的對象 arr “展開”為參數清單。

例如使用 Math.max:

let arr = [3, 5, 1];

alert( Math.max(...arr) ); // 5(Spread 操作符把數組轉為參數清單)      

我們同樣可以傳遞多個被展開的疊代對象:

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(...arr1, ...arr2) ); // 8      

我們甚至可以在普通的參數間使用展開操作符:

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25      

同樣,我們可以使用 Spread 操作符合并數組:

let arr = [3, 5, 1];
let arr2 = [8, 9, 15];

let merged = [0, ...arr, 2, ...arr2];

alert(merged); // 0,3,5,1,2,8,9,15(0,然後是 arr 的值,2,然後是 arr2 的值)      

在上面的例子中,我們都是使用數組來講解 Spread 操作符的,其實其他可周遊的對象也同樣适用。

如下例所示,我們可以使用 Spread 操作符把字元串展開為字元數組:

let str = "Hello";

alert( [...str] ); // H,e,l,l,o      

JavaScript 内部使用了周遊器來實作 Spread 操作符,是以使用 Spread 操作符展開對象與使用 for…of 周遊該對象是一緻的。

是以,針對一個字元串,for…of 會逐位傳回該字元串中的字元,…str 也同理會得到 “H”,“e”,“l”,“l”,“o” 這樣的結果。再将上一步所得的字元串序列傳入數組初始化操作符 […str],一個字元數組就這樣生成了。

我們還可以使用 Array.from 實作上述功能,因為該操作符會将可周遊對象(如字元串)轉換為數組:

let str = "Hello";

// Array.from 會将可周遊對象轉為數組
alert( Array.from(str) ); // H,e,l,l,o      

運作結果與 […str] 結果一緻。

不過需要注意的是使用 Array.from(obj) 和使用 […obj] 還是存在細微差别:

  • Array.from 同時适用于類數組對象和可周遊對象。
  • Spread 操作符隻能操作可周遊對象。

是以,若希望把一些“東西”轉為數組,使用 Array.from 将更為通用。

小結

當我們在代碼中遇到 “…” 時,它不是 Rest 參數就是 Spread 操作符。

我們可以使用下列方法區分二者:

  • 若 … 出現在函數的參數清單,那它表示的就是 Rest 參數,它會把函數多餘的實參收集到一個數組中。
  • 若 … 出現在函數調用或類似的表達式中,那它就是 Spread 操作符,它會把一個數組展開為逗号分隔的元素清單。

使用場景:

  • Rest 參數用于建立可接收任意個參數的函數。
  • Spread 操作符可以在函數調用傳參時,把含有參數的數組展開為函數需要的參數清單形式。

    這兩個操作符的出現友善了我們在參數數組和參數清單間來回轉換。