ES6+ 剩余參數
1. 前言
上節我們學習了展開語法,本節我們學習與之相反的操作 —— 剩余語法(Rest syntax 也可以叫剩余參數)看起來和展開語法完全相同都是使用 ...
的語法糖,不同之處在于剩余參數用于解構數組和對象。從某種意義上說,剩余語法與展開語法是相反的:展開語法將數組展開為其中的各個元素,而剩余語法則是將多個元素收集起來成為一個整體。
2. 函數參數
在講解剩余參數前,我們先來看看,剩余參數在函數參數中都解決了哪些問題?為什么會引入剩余參數的概念?
在 ES5 中,函數經常會傳入不定參數,在傳入不定參數時,ES5 的給出的解決方案是通過 arguments
對象來獲取函數調用時傳遞的參數。 arguments
對象不是一個數組,它是一個類數組對象,所謂類數組對象,就是指可以通過索引屬性訪問元素并且擁有 length 屬性的對象。
一個簡單的類數組對象是長這樣的:
var arrLike = {
0: 'name',
1: 'age',
2: 'job',
length: 3
}
而它所對應的數組應該是這樣子的:
var arr = ['name', 'age', 'job'];
這里我們說類數組對象與數組的性質相似,是因為類數組對象在訪問、賦值、獲取長度上的操作與數組是一致的,具體內容可查閱相關的類數組使用。
在函數體中定義了 Arguments 對象,其包含函數的參數和其它屬性,以 arguments
變量來指代。下面我們看個實例:
function fn() {
console.log(arguments);
}
fn('imooc', 7, 'ES6')
在控制臺中打印出上面的代碼結果,如下圖所示:在定義函數的時候沒有給定參數,但是通過 arguments
對象可以拿到傳入的參數。可以看到 arguments
中包含了函數傳遞的參數、length 等屬性,length 屬性表示的是實參的長度,即調用函數的時候傳入的參數個數。這樣我們就對 arguments
對象有了一定的了解。
在 ES5 的開發模式下,想要使用傳遞的參數,則需要按位置把對應的參數取出來。盡管 arguments
是一個類數組且可遍歷的變量,但它終究不是數組,它不支持數組方法,因此我們不能調用 arguments.forEeach (…)
等數組的方法。需要使用一些特殊的方法轉換成數組使用,如:
function fn() {
var arr = [].slice.call(arguments);
console.log(arr)
}
fn('ES6');
// ["ES6"]
fn('imooc', 7, 'ES6');
// ["imooc", 7, "ES6"]
終于借助 call
方法把 arguments
轉化成一個真正的數組了。但是這樣無疑是一個繁瑣的過程,而且不容易理解。這時 ES6 給出了它的完美解決方案 —— 剩余參數,那剩余參數是如何在函數傳參中使用的呢?下面我們來看看實例:
function fn(...args) {
console.log(args)
}
fn('ES6');
// ["ES6"]
fn('imooc', 7, 'ES6');
// ["imooc", 7, "ES6"]
使用方式很簡單在函數定義時使用 ...
緊接著跟一個收集的參數,這個收集的參數就是我們所傳入不定參數的集合 —— 也就是數組。這樣就很簡單地擺脫了 arguments 的束縛。另外,還可以指定一個默認的參數,如下示例:
function fn(name, ...args) {
console.log(name); // 基礎參數
console.log(args); // 剩下的參數組成的數組
}
fn('ES6');
// 'ES6'
// []
fn('imooc', 7, 'ES6');
// "imooc"
// [7, "ES6"]
上面的代碼中給函數第一個參數,聲明一個變量 name,剩余的參數會被 ...
收集成一個數組,這就是剩余參數。引入剩余參數就是為了能替代函數內部的 arguments
,由于 arguments
對象不具備數組的方法,所以很多時候在使用之前要先轉換成一個數組。而剩余參數本來就是一個數組,避免了這多余的一步,使用起來既優雅又自然。
2. 解構剩余參數
ES6 允許按照一定模式,從數組和對象中提取值,并對變量進行賦值,這被稱為解構(下節我們會講到)。 比如如下代碼:
let array = [1, 2, 3]
let [a, b, c] = array;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
再比如如下代碼:
let obj = {a:1, b:2, c:3}
let {a, b, c} = obj;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
上面的兩個例子,就是數組和對象的解構賦值過程,在解構賦值時,可以使用剩余操作符。剩余操作符所操作的變量會匹配在解構賦值中所有其他變量未匹配到的屬性??慈缦率纠?/p>
let {a, b, ...others } = {a: 1, b: 2, c: 3, d: 4, e: 5}
console.log(a); // 1
console.log(b); // 2
console.log(others); // {c: 3, d: 4, e: 5}
上面的代碼中,a、b 會匹配對象中對應的值,...others
則會收集匹配余下的屬性值,并打包起來構造一個新的對象賦值給了 others
。
數組也可以通過剩余操作符,把剩余的元素打包成一個新的數組賦值給剩余屬性,代碼如下:
let array = [1, 2, 3, 4, 5];
let [a, b, ...others] = array;
console.log(a); // 1
console.log(b); // 2
console.log(others); // [3,4,5]
在函數傳參的時候也可以是和解構一起使用。如下所示。
function fun(...[a, b, c]) {
return a + b + c;
}
fun('1') // NaN (b 和 c 都是 undefined)
fun(1, 2, 3) // 6
fun(1, 2, 3, 4) // 6 多余的參數不會被獲取到
上面的代碼中,a、b、c 會去解構傳入參數,加上有剩余語法的作用,對應的值從數組中的項解構出來,在函數內部直接使用解構出來的參數即可。剩余語法看起來和展開語法完全相同,不同點在于,剩余參數用于解構數組和對象。
3. 小結
本節結合了 ES5 函數中的 arguments
對象引入了為什么 ES6 會引入剩余參數的概念,可以看到剩余參數所帶來的好處。本節內容可以總結以下幾點:
- 剩余參數是為了能替代函數內部的 arguments 而引入的;
- 和展開語法相反,剩余參數是將多個單個元素聚集起來形成一個單獨的個體的過程。