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 操作符可以在函數調用傳參時,把含有參數的數組展開為函數需要的參數清單形式。
這兩個操作符的出現友善了我們在參數數組和參數清單間來回轉換。