點燈坊

失くすものさえない今が強くなるチャンスよ

ECMAScript 之 Array Destructuring

Sam Xiao's Avatar 2019-08-19

ECMAScript 2015 提供了很有創意的 Array Destructuring,原本需要好幾行才能完成的寫法,最後只需一行就能解決。

Version

macOS Mojave 10.14.5
VS Code 1.41.1
Quokka 1.0.267
ECMAScript 2015

Basic Assignment

let data = [1, 2, 3]

let x = data[0]
let y = data[1]
let z = data[2]

x // ?
y // ?
z // ?

data 為 array,想分別指定到 xyz 3 個 variable,ES5 必須寫 3 行。

let data = [1, 2, 3]

let [x, y, z] = data

x // ?
y // ?
z // ?

xyz 直接寫在 [] 內,array 會依照順序直接分解到 variable,ES6 只需 1 行。

array000

Batch Assignment

let x = 1
let y = 2

x // ?
y // ?

若要對多個單一 variable 指定 value,ES5 有幾個 variable 就要寫幾行。

let [x, y] = [1, 2]

x // ?
y // ?

可將所有 variable 與 value 都用 array 整理起來,ES6 只要一行即可搞定。

array020

Swap Variable

let x = 1
let y = 2

let temp = x
x = y
y = temp

x // ?
y // ?

若要兩個 variable 內容交換,ES5 會另外建立 temp 暫存變數,需要 3 行。

let x = 1
let y = 2;

[y, x] = [x, y]

x // ?
y // ?

ES6 將 variable 寫在 [] 直接交換,只要 1 行,可讀性非常高。

array014

Extract Array

let data = []

let name = 'Sam'
if (data.length > 0)
  name = data[0]

name // ?

ES5 若想將 array 內 element 指定為 variable,但又不確定 array 是否有該 element,傳統會先對 variable 指定 default value,並檢查 array 的 length property,若確定 length 如預期,再使用 index 存取 array 指定給 variable。

let data = []

let [name = 'Sam'] = data

name // ?

ES6 可直接在 array destructuring 時指定 default value,不需再搭配 if 判斷。

array015

let data = ['John', 'Kevin']

let [_, _, name = 'Sam'] = data

name // ?

若要分解的 variable 不是第一個 element 也沒關係,只要使用 , 留出空位即可,_ 並非必須,但有助於可讀性,亦可同時指定 default value。

array016

Clone Array

let data0 = [1, 2, 3]
let data1 = data0

data1 // ?
data0 === data1 // ?

若要複製 array,直覺會使用 =,但 array 並非 primitive,而是 object,因此只是複製記憶體位置,所以 data0data1 仍然指向同一個 [1, 2, 3],而不是真的 clone array。

array002

let data0 = [1, 2, 3]
let data1 = data0.slice()

data1 // ?
data0 === data1 // ?

ES5 可使用 Array.prototype.slice() 將 array 加以複製,slice() 是真的回傳一個新 array,而非指向同一個 array。

array003

let data0 = [1, 2, 3]
let data1 = data0.concat()

data1 // ?
data0 === data1 // ?

ES5 也可以使用 Array.prototype.concat() 複製一個新 array,與 slice() 結果相同。

array004

let data0 = [1, 2, 3]
let [...data1] = data0;

data1; // ?
data0 === data1; // ?

ES6 則有新的寫法,array destructuring 配合 ... spread operator,其結果等同於 ES5 的 slice()concat()

array001

let data0 = [1, 2, 3]
let data1 = [...data0]

data1 // ?
data0 === data1 // ?

亦可將 ... 寫在右側展開亦可。

array021

let data0 = [1, 2, 3]
let [_,...data1] = data0

data1 // ?

若要 clone 的只是部分 array,可使用 , 空出 space 即可。

array017

Extract Character

let str = 'Chinese'
let [firstChar] = str

firstChar // ?

Destructuring 除了可以用在 array 外,事實上也能用在 iterable,最簡單的 iterable 就是 string,可用來將 string 中特定 char 指定到 variable。

array018

let str = 'Chinese'
let [_, secondChar] = str

secondChar // ?

若要分解的 variable 不是第一個 index 也沒關係,只要使用 , 留出空位即可。

array019

Function Parameter

let data = [1, 2, 3]

let fn = function(args) {
  let a = args[0]
  let b = args[1]
  let c = args[2]

  console.log(a, b, c)
}

fn(data)

若 parameter 為 array,ES5 會使用 array index 一一的指定給 variable,需要 3 行。

let data = [1, 2, 3]

let fn = ([a, b, c]) => console.log(a, b, c)

fn(data)

ES6 可在 parameter 使用 array destructuring 直接將 array 分解成 variable。

雖然只是一個 array,但若使用 arrow function 時,仍須加上 () 將 array 刮起來。

array006

let data = [1, 2, 3]

let fn = ([a, b, c, d = 4]) => console.log(a, b, c, d)

fn(data)

若想提供 default value 也行,使用 = 提供 default value 即可。

array007

let data = ['RGB', [255, 0, 255]]

let fn = function(args) {
  let name = args[0]
  let red = args[1][0]
  let green = args[1][1]
  let blue = args[1][2]

  console.log(name, red, green, blue);
}

fn(data)

若 parameter 是 nested array,在 ES5 必須一一分解 variable,需要 4 行。

let data = ['RGB', [255, 0, 255]]

let fn = ([name, [red, green, blue]]) => console.log(name, red, green, blue)

fn(data)

ES6 也可直接在 parameter 分解 nested array。

array012

Function Return Array

let fn = function() {
  return [1, 2, 3]
}

let result = fn()
let a = result[0]
let b = result[1]
let c = result[2]

a // ?
b // ?
c // ?

若要讓 function 回傳多值,在 ES5 正規解法就是回傳 array,呼叫端接到 array 後,再使用 array index 一一指定給 variable,需要多行。

let fn = () => [1, 2, 3]

let [a, b, c] = fn()

a // ?
b // ?
c // ?

在 ES6 可使用 array destructuring 接收 function 回傳的 array,並自動分解成 variable,只需 1 行。

array009

let fn = () => [1, 2, 3]

let [a, _, c] = fn()

a // ?
c // ?

若並不想分解 function 所傳回所有 array,可以在 ,, 之間空著,會自行忽略分解之。

array010

let fn = () => [1, 2, 3]

let [a, ...c] = fn()

a // ?
c // ?

若 function 回傳的 array,想部分也分解成 array,可搭配 ... spread operator。

但這種寫法必須寫在 array destructuring 的最後才行

array011

Conclusion

  • Array destructuring 的基本精神就是 一個蘿蔔一個坑,ES6 會自動將 array 分解成 variable
  • 若要同時將多值給不同 variable,可將所有 variable 與 value 都用 array 整理起來,只要一行即可
  • Swap variable 算是 array destructuring 很巧妙的應用
  • Array destructuring 配合 spread operator,可以取代原本 slice()concat() 才能 clone array
  • 事實上只要是 iterable 就能使用 destructuring,而 string 就是最簡單的 iterable,因此也能使用 destructuring
  • Array destructuring 配合 function parameter 特別好用,可以直接在 parameter 中就分解成 variable,還可搭配 default value
  • Array destructuring 可將 function 回傳的 array 直接分解到 variable

Reference

MDN, Destructuring assignment
Glad Chinda, ES6 Destructuring: The Complete Guide
Liam Hurst, 5 Interesting Uses of JavaScript Destructuring