擴展運算符(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;
}
};
~~~
*****
> 原文出處:
[博客園-ES6躬行記](https://www.cnblogs.com/strick/category/1372951.html)
[知乎專欄-ES6躬行記](https://zhuanlan.zhihu.com/pwes6)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020