[第12期]深入了解強大的 ES6 「 … 」 運算符
- 2020 年 3 月 3 日
- 筆記
引言
我沒有上班摸魚,假期還沒結束,明天才上班。
在家寫了一篇比較基礎的文章,花了好幾個小時, 在這分享給大家。
背景
...
運算符, 是ES6
里一個新引入的運演算法, 也叫展開/收集
運算符, 我們每天都要和它打交道。
這篇文章,我就帶你系統的回顧下這個運算符, 介紹一些基礎
和進階
的用法。
基礎篇
先看一下官方描述:
Spread syntax allows an
iterable
, such as anarray expression
orstring
, to be expanded in places where 0 or morearguments
orelements
are expected or anobject expression
to be expanded in places where 0 or more key-value pairs (for object literals) are expected.
簡而言之就是, ...
運算符可以展開一個可迭代
對象的所有項。
可迭代的對象一般是指可以被循環的, 包括: string
, array
, set
, 等等。
下面我們來看幾個基礎的例子來加深理解。
基礎用法
基礎用法1: 展開
const a = [2, 3, 4] const b = [1, ...a, 5] b; // [1, 2, 3, 4, 5]
基礎用法2: 收集
function foo(a, b, ...c) { console.log(a, b, c) } foo(1, 2, 3, 4, 5); // 1, 2, [3, 4, 5]
如果沒有命名參數的話, ...
就會收集所有的參數:
function foo(...args) { console.log(args) } foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
關於這個收集
的用法, 官方描述:
「A function』s
last parameter
can be prefixed with … which will cause all remaining (user supplied) arguments to be placed within a "standard" javascript array.Only the last parameter can be a rest parameter.」
這個運算符一定是在最後一個參數的位置, 也很好理解, 就是「收集前面剩下的參數」。
Remember that the rest parameter
must be the last parameter
, or anerror
will occur.
如果不在最後一位, 會報錯。
不得不感嘆, 這個運算符設計的真的是妙, 可展開
, 可收集
, 收放自如
, 當真好用
。
基礎用法3: 把類數組
轉換為數組
擴展運算符「...
」也可以將某些數據結構轉為數組.
先回顧下什麼是類數組
吧.
類數組和數組非常接近, 都可以擁有一系列元素, 也有length
屬性, 最大的不同是:
類數組不具備數組的一系列方法。
舉個例子:

使用 ...
就可以實現類數組到數組的轉換, 轉換之後, 就可以使用數組的各種方法了。
你還記得在這個操作符出來之前是如何轉換的嗎?
這個問題還是頭條的一個前端面試題
。
看例子:
// ES5 時代 function bar() { var args = Array.prototype.slice.call(arguments); // 調用push 加幾個元素 args.push(1, 2, 3); // 把args 作為參數傳遞給foo foo.apply(null, args) } // ES6 時代 function foo(...args) { // 搜集參數到 args args.push(4, 5, 6) console.log(...args) // 展開args } bar(0); // 0 1 2 3 4 5 6
基礎用法4: 增加元素或屬性
1: 為數組新增成員
const pokemon = ['KK', 'Peter']; const charmander = '鄭伊健'; const pokedex = [...pokemon, charmander]; console.log(pokedex); //Result: [ 'KK', 'Peter', '鄭伊健' ]
2: 為對象新增屬性
const basicSquirtle = { name: 'Squirtle', type: 'Water' }; const fullSquirtle = { ...basicSquirtle, species: 'Tiny Turtle', evolution: 'Wartortle' }; console.log(fullSquirtle); //Result: { name: 'Squirtle', type: 'Water', species: 'Tiny Turtle', evolution: 'Wartortle' }
基礎用法5: 合併數組/對象
合併數組:
const pokemon = ['Squirtle', 'Bulbasur', 'Charmander']; const morePokemon = ['Totodile', 'Chikorita', 'Cyndaquil']; const pokedex = [...pokemon, ...morePokemon]; console.log(pokedex); //Result: [ 'Squirtle', 'Bulbasur', 'Charmander', 'Totodile', 'Chikorita', 'Cyndaquil' ] // 對象數組也一樣: const pokemon = [ { name: 'Squirtle', type: 'Water' }, { name: 'Bulbasur', type: 'Plant' } ]; const morePokemon = [{ name: 'Charmander', type: 'Fire' }]; const pokedex = [...pokemon, ...morePokemon]; console.log(pokedex); //Result: [ { name: 'Squirtle', type: 'Water' }, { name: 'Bulbasur', type: 'Plant' }, { name: 'Charmander', type: 'Fire' } ]
合併對象
const baseSquirtle = { name: 'Squirtle', type: 'Water' }; const squirtleDetails = { species: 'Tiny Turtle Pokemon', evolution: 'Wartortle' }; const squirtle = { ...baseSquirtle, ...squirtleDetails }; console.log(squirtle); //Result: { name: 'Squirtle', type: 'Water', species: 'Tiny Turtle Pokemon', evolution: 'Wartortle' }
以上是一些基礎費用法
下面介紹一些...
操作符的進階用法。
進階篇
1. 複製具有嵌套結構的數據/對象
先看一個例子:
const pokemon = { name: 'Squirtle', type: 'Water', abilities: ['Torrent', 'Rain Dish'] }; const squirtleClone = { ...pokemon }; pokemon.name = 'Charmander'; pokemon.abilities.push('Surf'); console.log(squirtleClone); //Result: { name: 'Squirtle', type: 'Water', abilities: [ 'Torrent', 'Rain Dish', 'Surf' ] }
當我們修改原對象
的name
屬性時,我們的克隆對象
的 name
屬性沒有受影響
, 這是符合我們預期的。
但是當修改原對象
的abilities
屬性時,我們的克隆對象也被修改
了。
原因也很簡單, 因為複製過來的abilities 是一個引用類型, 原數據改了, 用到他的地方也會跟著改。
知道原因,再解決就很簡單了, 兩種方式:
1: 複製引用類型的數據
const pokemon = { name: 'Squirtle', type: 'Water', abilities: ['Torrent', 'Rain Dish'] }; const squirtleClone = { ...pokemon, abilities: [...pokemon.abilities] }; pokemon.name = 'Charmander'; pokemon.abilities.push('Surf'); console.log(squirtleClone); //Result: { name: 'Squirtle', type: 'Water', abilities: [ 'Torrent', 'Rain Dish' ] }
這樣就OK了
2: 深克隆
在這裡就不多解釋了。
2: 增加條件屬性
顧名思義, 就是需要根據條件添加的屬性。
看個例子:
const pokemon = { name: 'Squirtle', type: 'Water' }; const abilities = ['Torrent', 'Rain dish']; const fullPokemon = abilities ? { ...pokemon, abilities } : pokemon; console.log(fullPokemon);
也可以簡化一下:
const fullPokemon = abilities && { ...pokemon, abilities };
3: 短路
const pokemon = { name: 'Squirtle', type: 'Water' }; const abilities = ['Torrent', 'Rain dish']; const fullPokemon = { ...pokemon, ...(abilities && { abilities }) }; console.log(fullPokemon);
如果 abilities
為 true
, 就相當於是
這也是一個很有用的技巧。
3: 默認結構和添加默認屬性
默認解構:
我們知道, 當結構一個對象的時候, 如果這個對象里沒有某個屬性, 解出來是undefined
, 我們可以添加默認值來解決:
const pokemon = { id: 1, name: 'Squirtle' }; const { type, name } = pokemon; console.log(name); //Result: Squirtle console.log(type); //Result: undefined //Assigning default value to the type variable const { type = 'Water', name } = pokemon; console.log(type); //Result: Water
添加默認屬性
有時候從我們會遇到這樣的情況, 一個對象, 大部分屬性是相似的,只有小部分是不不同的,這時候我們就可以設置一個基礎對象, 具備基礎屬性, 其他的對象可以通過擴展這個對象來得到。
看例子:
const pokemon = { name: 'Squirtle', type: 'Water' }; // 給abilities默認賦值 const { abilities = [], ...rest } = pokemon; const fullSquirtle = { ...rest, abilities }; console.log(rest); //Result: { name: 'Squirtle', type: 'Water' } console.log({ fullSquirtle }); //Result: { name: 'Squirtle', type: 'Water', abilities: [] }
這裡就是通過 展開 rest
, 合併 abilities
得到完全體的數據。
如果有批量的數據需要處理,這種方法也非常方便:
const pokemon = [ { name: 'Charmander', type: 'Fire' }, { name: 'Squirtle', type: 'Water', abilities: ['Torrent', 'Rain Dish'] }, { name: 'Bulbasur', type: 'Plant' } ]; function setDefaultAbilities(object) { const { abilities = [], ...rest } = object; return { ...rest, abilities }; } // Applying the setDefaultAbilities function to all the pokemon in the array: const normalizedPokemon = pokemon.map(pokemon => setDefaultAbilities(pokemon)); console.log(normalizedPokemon); //Result: [ { name: 'Charmander', type: 'Fire', abilities: [] }, { name: 'Squirtle', type: 'Water', abilities: [ 'Torrent', 'Rain Dish' ] }, { name: 'Bulbasur', type: 'Plant', abilities: [] } ]
這樣迭代一遍, 所有的對象就都具備 abilities
屬性了。
總結
...
運算符非常靈活, 收放自如,非常強大, 希望我們都能很好的掌握這個工具。
內容就這麼多,希望對大家有所幫助, 如有紕漏, 歡迎指正。
最後
覺得內容有幫助可以關注下我的公眾號 「 前端e進階 」,一起學習成長
關注我啦
可以通過公眾號菜單欄的聯繫我
, 了解我們的微信群
, 謝謝。
我就知道你「在看」