ECMAScript 2015 的 Spread Operator 是很有創意的發明,讓很多操作都簡化成 ...
即可,且可讀性更佳。
Version
macOS Catalina 10.15.5
VS Code 1.46.1
ECMAScript 2015
Clone Array
import { equals } from 'ramda'
let arr1 = [1, 2, 3]
let arr2 = [...arr1]
arr1 === arr2 // ?
equals(arr1, arr2) // ?
欲將 arr1
clone 到 arr2
,注意是 clone 而非 copy,可在 []
間使用 ...
將 arr1
的 element 展開即可 clone。
使用 ===
判斷為 false
,可見 arr1
與 arr2
的 reference 並不相同,因此並非 copy reference。
使用 Ramda 的 equals()
判斷為 true
,可見 arr1
與 arr2
的 value 相同,因此為 clone Array。
若使用 assignment operator
=
,則為 copy reference,因此===
與equals()
皆回傳true
import { clone } from 'ramda'
let arr1 = [
1,
[3, 4],
]
let arr2 = [...arr1] // ?
let arr3 = clone(arr1) // ?
arr1 === arr2 // ?
arr1 === arr3 // ?
arr1[1] === arr2[1] // ?
arr1[1] === arr3[1] // ?
不過 ...
只是 shallow clone,也就是第一層 Array 為 clone,第二層之後的 Array 為 copy reference。
arr1
內包含 Nested Array [3, 4]
,其中 arr2
使用 ...
,而 arr3
使用 Ramda 的 clone()
。
無論使用 ...
或 clone()
,arr2
與 arr3
的 reference 皆與 arr1
不同:
arr2[1]
與arr1[1]
的 reference 相同,顯然...
是 shallow clonearr3[1]
與arr1[1]
的 reference 不同,顯然clone()
deep clone
Spread operator 雖然 clone 很方便,但僅是 shallow clone,若要 deep clone 則要使用 Ramda 的
clone()
Merge Array
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
let arr3 = [...arr1, ...arr2] // ?
欲將 arr1
與 arr2
合併,可在 []
內將 arr1
與 arr2
的 element 使用 ...
展開,中間加上 ,
即可合併。
Array 合併時亦有
...
shallow clone 問題需注意
Pass Arguments as Array
let data = [1, 2, 3]
let f = (x, y, z) => x + y + z
f(...data) // ?
若原本 function 為 n 個 argument,但資料卻是 n 個 element 的 Array,可使用 ...
將 Array element 展開傳入 function。
Clone Object
import { equals } from 'ramda'
let obj1 = { name: 'Sam' }
let obj2 = { ...obj1 }
obj1 === obj2 // ?
equals(obj1, obj2) // ?
...
除了用於 Array,亦可用於 Object。
欲將 obj1
clone 到 obj2
,注意是 clone 而非 copy,可在 {}
間使用 ...
將 obj1
的 property 展開即可 clone。
使用 ===
判斷為 false
,可見 obj1
與 obj2
的 reference 並不相同,因此並非 copy reference。
使用 Ramda 的 equals()
判斷為 true
,可見 obj1
與 obj2
的 property 相同,因此為 clone object。
若使用 assignment operator
=
,則為 copy reference,因此===
與equals()
皆回傳true
import { clone } from 'ramda'
let obj1 = {
id: 1,
name: {
first: 'Sam',
last: 'Xiao'
}
}
let obj2 = {...obj1} // ?
let obj3 = clone(obj1) // ?
obj2 === obj1 // ?
obj3 === obj1 // ?
obj1.name === obj2.name // ?
obj1.name === obj3.name // ?
不過 ...
只是 shallow clone,也就是第一層 object 為 clone,第二層之後的 object 為 copy reference。
obj1.name
為 object,其中 obj2
使用 ...
,而 obj3
使用 Ramda 的 clone()
。
無論使用 ...
或 clone()
,obj2
與 obj3
的 reference 皆與 obj1
不同。
obj2.name
與obj1.name
的 reference 相同,顯然...
是 shallow cloneobj3.name
與obj1.name
的 reference 不同,顯然clone()
deep clone
Spread operator 雖然 clone 很方便,但僅是 shallow clone,若要 deep clone 則要使用 Ramda 的
clone()
Merge Object
let obj1 = { firstName: 'Sam' }
let obj2 = { lastName: 'Xiao'}
let obj3 = { ...obj1, ...obj2 } // ?
欲將 obj1
與 obj2
合併,可在 {}
內將 obj1
與 obj2
的 property 使用 ...
展開,中間加上 ,
即可合併。
Object 合併時亦有
...
shallow clone 問題需注意
Rest Parameter
let sum = (...args) => args.reduce((a, x) => a + x, 0)
sum(1, 2, 3) // ?
若要實現 function 有 無限 argument
,可在 argument 名稱前加上 ...
,此時 args
為真正 Array,而非 Array-like Object,因此可使用 Array.prototype
下的 method。
使用 rest parameter 時,儘管 arrow function 的 argument 看起來只有一個,但也要加上
()
,否則會誤判
String to Array
let str = 'Hello' // ?
let arr = [...str] // ?
Spread operator 亦支援 Iterable,而 String 就是最簡單的 Iterable,因此可將 String 轉成 Char Array。
Set to Array
let set = new Set([1, 2, 3]) // ?
let arr = [...set] // ?
Set 亦為 Iterable,因此 ...
亦可將 Set 轉成 Array。
Node List to Array
<template>
<div id="app">
<p>Hello</p>
<p>World</p>
</div>
</template>
<script>
let mounted = function() {
let nodeList = document.querySelectorAll('p')
let arr = [...nodeList]
arr.forEach(x => console.log(x.innerText))
}
export default {
name: 'App',
mounted
}
</script>
第 10 行
let nodeList = document.querySelectorAll('p')
let arr = [...nodeList]
arr.forEach(x => console.log(x.innerText))
使用 document.querySelectorAll()
取得所有 HTML tag 為 <p>
的 DOM Element,其回傳為 Node List,並不是 Array。
可使用 ...
將 Node List 轉成 Array,因此才能使用 forEach()
。
Convert Arguments to Array
let f = function() {
return [...arguments].map(x => `Hello ${x}`)
}
f('Sam', 'John') // ?
arguments
只是 Array-like object,可以使用 ...
轉成 Array 後,方可使用 map()
。
arguments
只能用在 function expression,不能在 arrow function
Conclusion
- 關於 clone Array,ES5 可用
Array.prototype.slice()
,ES6 也可用Array.from()
- 關於 merge Array,ES5 可用
Array.prototype.concat()
- 關於 pass argument as Array,ES5 可用
Function.prototype.apply()
- 關於 clone 與 merge object,ES5 可用
Object.prototype.assign()
- 關於 rest parameter,ES5 可使用 function 自帶的
arguments
variable,注意其為 Array-like Object 與 iterable,而非真正 Array - Spread operator 還可用在 Iterable,如 String、Set、Node List 與 Array-like Object
Reference
Adam Daniels, Top 5 Uses for the Spread Operator in JavaScript
Samantha Ming, 6 Use Case of Spread with Array in JavaScript
MDN, Spread syntax
MDN, Rest parameters