點燈坊

學而時習之,不亦悅乎

如何 Clone Array ?

Sam Xiao's Avatar 2019-08-14

= 只能 Copy Array 的 Reference,而非真正 Clone Array,本文整理出 4 種方式,並詳細分析其特色。

Version

macOS Mojave 10.14.5
VS Code 1.37.0
Quokka 1.0.240
ECMAScript 2015
Ramda 0.26.1

Assignment Operator

import { equals } from 'ramda';

let arr1 = [1, 2, 3];
let arr2 = arr1;

arr1 === arr2; // ?
equals(arr1, arr2); // ?

直覺會使用 = assignment operator。

clone000

使用 === 判斷為 true,可見 arr1arr2 的 reference 相同,因此為 copy reference,並非 clone array。

使用 Ramda 的 equals() 判斷為 true,由於 arr1arr2 的 reference 相同,value 當然相同。

Array.prototype.slice()

import { equals } from 'ramda';

let arr1 = [1, 2, 3];
let arr2 = arr1.slice();

arr1 === arr2; // ?
equals(arr1, arr2); // ?

在 ES5 若要 clone array,必須使用 Array.prototype.slice()

Array.prototype.slice([begin[, end]])
將整個 array 或部分 array 做 shallow clone

  • begin:起始 index
  • end:結束 index (不包含)

若不指定 beginend,則相當於 clone 整個 array。

若指定 beginend,則 clone 部分 array。

clone001

使用 === 判斷為 false,可見 arr1arr2 的 reference 並不相同,因此並非 copy reference。

使用 Ramda 的 equals() 判斷為 true,可見 arr1arr2 的 value 相同,因此為 clone array。

Array.from()

import { equals } from 'ramda';

let arr1 = [1, 2, 3];
let arr2 = Array.from(arr1);

arr1 === arr2; // ?
equals(arr1, arr2); // ?

ES6 引進 Array.from(),專門負責將 array-like object 與 iterable 轉成 array。

因為 array 亦是 iterable,因此也能用於 Array.from(),由於回傳為新 array,意外地也算 clone array。

clone002

Spread Operator

import { equals } from 'ramda';

let arr1 = [1, 2, 3];
let arr2 = [...arr1];

arr1 === arr2; // ?
equals(arr1, arr2); // ?

ES6 亦引進了 ... spread operator,可將 array element 展開,若放在 [] 內,也能實現 clone array。

clone003

Ramda

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]; // ?

無論 Array.prototype.slice()Array.from() 或 spread operator,都只能對 array 做 shallow clone,也就是第一層 array 為 clone,第二層之後 array 為 copy reference。

arr1 內包含 nested array [3, 4],其中 arr2 使用 ...,而 arr3 使用 Ramda 的 clone()

clone004

無論使用 ...clone()arr2arr3 的 reference 皆與 arr1 不同。

  • arr2[1]arr1[1] 的 reference 相同,顯然 ... 是 shallow clone
  • arr3[1]arr1[1] 的 reference 不同,顯然 clone() deep clone

Conclusion

  • 若要 shallow clone,則 ... spread operator 是可讀性最高寫法;若要 deep clone,則要使用 Ramda 的 clone()

Reference

Samantha Ming, ES6 Way to Clone an Array
MDN, Array.prototype.slice()
MDN, Array.from()
MDN, Spread syntax