ES20XX Modern JS 新語言特性筆記 | Spread, Rest & Destructuring

2021-08-02

筆記從 ES2015 / ES2016 / ES2017 / ES2018 / ES2019 / ES2020 以來,JS 所加入的新語言特性。使用的情境新語言特性不僅是為了減少對第三方套件的依賴 (Lodash),也是基於 ESLint 上 Clean Code、Best Practices 規範要求。熟悉新語言特性不僅能夠使 JS 開發更加快速也能夠提升閱讀性。

JavaScript logo

說明

本篇筆記為 Modern JS 所相關的 Spread、Rest、Destructuring 語法,同樣都是使用了 ... 的符號,但卻有不同的情境語意。

Rest Parameters

在參數中使用 Three Dots 修飾參數,可以將複數個引數(Arguments)轉換為 Array,稱為 Rest Parameters。Rest Parameters 必須在複數參數最後順序使用,且僅允許一個 Rest Parameters。

Rest Parameters 的使用位置在 fucntion 的參數宣告區塊,使用的情境如 function 可以將引入包裝成一個 Array,以方便使用 Array 相關的 Method 進行處理。

const countIf = function (minimum, ...args){
  return args.filter(e => e > minimum).length;
}
countIf(3, 2, 3, 4, 5, 6)
// 3

Spread Operator

不同於 Rest 是將數個引數集合成 Array,Spread 則是將陣列中的各元素拆解為引數。Spread Operator 使用的位置在 Array 當中,用於展開其他 Array 的 elements。

Spread Operator unpacks elements, while Rest Parameter packs elements into an array.

例如在 Literal Array 可以將其他的 Array Element 加入其中:

const even = [2, 4, 6]
const combined = [1, 3, 5, ...even]
// [1, 3, 5, 2, 4, 6]

或者可以為作為合併 Array 的使用目的:

const even = [2, 4, 6]
const odd = [1, 3, 5]
const numbers = [...even, ...odd]
// 2, 4, 6, 1, 3, 5

也可以用於複製 Array,但是 Shadow Copy 而非 Deep Copy,如果 Array 中的 element 為 Object 只會複製參照,而不會重新產生新的 Object。

let games = [
  {name: 'DQB2', qty: 1}, 
  {name: 'DQ5', qty: 2}
]
let copyGames = [...games]

copyGames[0].qty = 2;
// games' object is affteced
console.log(games[0].qty)
// 2

Destructuring Array

Destructuring 可以用於為變數賦值。

[a, b, c] = [1, 2, 3]
//a = 1, b = 2, c = 3
[e, f] = [4, 5, 6]
//e = 4, f = 5
[g, h] = [7]
//g = 7, h = undefined

Rest

也可以用 Rest 的方式,將部分的值另外集合起來作為新的 Array。

[x, y, ...arr] = [1, 2, 3, 4, 5]
// x = 1, y = 2, arr = [3, 4, 5]

default value

[x = 10, y = 15] = [20]
//x = 20, y = 15

Nested Array Mapping

巢狀的 Array 在解構賦值上也有對應的方式。

[
  name, 
  job, 
  [item1, item2, item3]
] = 
[
  'Loto', 
  'Warrior', 
  ['Flame Sword', 'Full Plate', 'Herb']
]
// name = 'Loto'
// job = 'Warrior'
// item1 = 'Flame Sword'
// item2 = 'Full Plate'
// item3 = 'Herb'

交換數值的應用

在 python 可以輕鬆交換變數數值的功能,藉由 Destructuring 也可以輕鬆處理囉 😋

let x = 2, y = 7;
[x, y] = [y, x]
// x = 7, y = 2

擴充函式回傳的彈性

讓函式可以回傳多個數值,並且用 Array 包裝,接收端則使用 Destructuring 的方式分別賦值給予變數。

const stringFactory = (str) => [str.toUpperCase(), str.toLowerCase()]
[upper, lower] = stringFactory('Hello World');
// upper = 'HELLO WORLD'
// loewr = 'hello world'

Spread & Destructuring Objects

ES2018 開始,不僅能夠針對 Array 進行 Spread 及 Destructuring,JS 對於 Object 也支援 Spread 及 Destructuring。

Spread

可以用於合併物件的屬性,例如想要把各學習階段物件的知識技能傳承下去:

const elementrySchool = {read: 6, calculate: 3}
const juniorHighSchool = {readEng: 3, calculate: 5}
const seniorHighSchool = {readEng: 7, calculate: 9, coding: 2}

let now = {...elementrySchool, ...juniorHighSchool, ...seniorHighSchool}
// { read: 6, calculate: 9, readEng: 7, coding: 2 }

如果有相同名稱的 properties 則順序很重要,後進行 destructuring 的值會覆寫先前的值。

let now = {...juniorHighSchool, ...seniorHighSchool, ...elementrySchool}
// { readEng: 7, calculate: 3, coding: 2, read: 6 }

Spread 也可以用於 shallow copy 的方式產生新的 Object:

const slime = {color: 'blue', hp: 3, mp: 1}
const redSlime = {...slime, color: 'red'}
// { color: 'red', hp: 3, mp: 1}

Destructuring

這是使用 Destructuring 在語法上比較特別的地方,當 slime Object 有提供 hp property 時,會使用該 property 作為 healthPoint variable 的數值,否則就會以預設值 5 作為 healthPoint 的數值。

來自解構對象的名稱要在冒號之前,賦值的變數值名稱則是在冒號之後:

const slime = {color: 'blue', hp: 3, mp: 1}
let {hp: healthPoint = 5, fast = 3} = slime
// healthPoint = 3
// fast = 3

此外如果 Destructuring 右側的物件有同名的 Property 與左側解構的名稱相同,則會自動以右側解構的值進行覆寫:

const slime = {color: 'blue', hp: 3, mp: 1, fast: 5}
let {hp: healthPoint = 5, fast = 3} = slime
// healthPoint = 3
// fast = 5

對於 null destructuring 以及 nested 的處理都如同 Array 的支援 🙄

Real Example Code

用途是從 localStorage 將資料讀回,並寫回 Vue 的 Data 之中,其中因為 wrongs 儲存的是 Set,所以另以賦值的方式處理。

/*
appSavings in localStorage
appSavings | {"corrects":[119,309],"wrongs":[800,1069,489],"completed":5}
*/

if (localStorage.appSavings !== undefined) {
  const appSavings = JSON.parse(localStorage.appSavings);

  ({ corrects: this.corrects, completed: this.completed } = appSavings);
  this.wrongs = new Set(appSavings.wrongs);
}

ECMA JavaScript ES20XX 系列筆記

  1. Array

  2. Spread, Rest & Destructuring

參考資料

ESLint Rules

JavaScript Tutorial

ES20XX

JavaScript: What’s new in ECMAScript 2019 (ES2019)/ES10?

ES2020 中 Javascript 10 個你應該知道的新功能

Node.js Dancing with ES20XX

查詢 Node.js 各版本對於 Ecma JavaScript 的支援情況。目前主要使用的 Node.js 12.10 對於 ES2019 以前都有相當高的支援情況,所以使用新 Feature 無煩惱。

Node.green