JavaScriptで注意が必要なことの1つが配列やオブジェクトのコピーです。
間違った方法でコピーしてしまうと予期せぬ動作やバグの原因にもなりかねません。
そこでここでは配列を完全にコピーするための本当に正しい方法を解説します。
配列をコピーするときにやりがちなNG例
まず配列コピーで絶対してはいけないNG例を紹介します。
そのNG例とは配列を別の変数にそのまま代入してしまうこと
例えば次のように配列をコピーしている(つもり)のコード例があるとしましょう。
1 2 |
var original = ['A', 'B', 'C']; var cloned = original; |
上コードでは cloned = original; のように別変数に配列を代入しています。一見正しそうですが実は全く意味をなさない間違ったコードです。
本当に間違えているかは次のコードを書いて実行してみれば分かるはず
1 2 3 4 5 6 |
cloned[0] = 'X'; cloned[1] = 'Y'; cloned[2] = 'Z'; console.log(original); console.log(cloned); |
コピー先の配列 cloned を書き換え、 original と cloned の両方をコンソール表示しています。
そして次がコンソール表示された original の内容、
1 |
[ 'X', 'Y', 'Z' ] |
そして次がコンソール表示された cloned の内容
1 |
[ 'X', 'Y', 'Z' ] |
・・・見てわかるように cloned の変更が original には反映されちゃってます。
こうなる理由は代入によって浅いコピー(Shallow Copy)が起きてしまったためです。
普通の数値などの変数の場合、別変数に代入すれば完全に別物として扱われます。しかし配列(あるいはオブジェクト)の場合はコピーと言っても・・・
- 深い(ディープ)コピー【Deep Copy】
- 浅い(シャロー)コピー【Shallow Copy】
・・・の2種類があります。
シャローコピーの場合はオリジナル配列とクローン配列は同じ参照(データのある場所)を共有しているにすぎません。そのため一方を変更すればもう一方も変更されてしまうという訳です。
ここら辺はポインタの概念がある言語(例えばC言語など)を学べば分かりやすいですが、そういうものだと思っておけば問題ありません。
なので結論としては完全に配列をコピーするにはディープコピーする必要があるという訳です。
配列を深い(ディープ)コピーするには
*訂正*
コメントでご指摘いただいた通り、Array.from は浅いコピーでした。なのでディープコピーという言葉を使うのは正しくなかったです。以下は コピー時に元配列に反映させない方法になります。
ではJavaScriptで配列(Array)をディープコピーするにはどうすればいいか・・・
という話ですが Array.from という配列専用のコピーメソッド を使えばOKです。
使い方はコピー元の配列を original として次のように書くだけ
1 |
var cloned = Array.from(original); |
これで cloned が original の ディープコピーして作成されます。
例えば次は Array.from を使い配列をコピーし、コピー元・先をコンソール表示するコード例
1 2 3 4 5 6 7 8 9 |
var original = ['A', 'B', 'C']; var cloned = Array.from(original); cloned[0] = 'D'; cloned[1] = 'E'; cloned[2] = 'F'; console.log(original); console.log(cloned); |
このコードを実行すると cloned が original の浅いコピー配列として作成されます。
そして次がコピー元の配列 original のコンソール表示、
1 |
[ 'A', 'B', 'C' ] |
そして次がコピー先の配列 cloned のコンソール表示です。
1 |
[ 'D', 'E', 'F' ] |
・・・今度は無事 cloned の変更が original に反映されないようになりました。
配列をコピーするときは、この例のようにディープコピーしてあげれば問題ありません。
もし配列代入を使ってしまうと、予想しない動作が起こるので要注意です。
意図的でない場合以外は基本的には
Array.from を使うのがほとんどだと思います。
連想配列をコピーする場合もディープコピーが必要
ここまでで配列のコピー方法についてまとめてきました。
コピー種類にはシャローとディープの2つがあり、データも含めて完全コピーするにはディープコピーするのを忘れないことが重要です。
そして配列同様、連想配列(オブジェクト)の場合も完全コピーにディープコピーが必要になります。
その詳しいやり方やコード例は次記事でまとめた通り
配列と同じく連想配列のコピーでもコピー方法には要注意です。
ここまでのまとめ
ということで配列をコピーする方法まとめ
- コピーにシャローコピーを使ってはダメ
浅いコピーだとコピー元とコピー先の参照(データのある場所)が共有されてしまう
そのため一方を変更するともう一方も変更されてしまうことに要注意! - コピーは基本ディープコピーを使う
配列の場合、ディープコピーするには Array.from を使えばOK
使用することでコピー元とコピー先の配列が完全に別のものとして扱える
浅いコピー(シャローコピー)が必要になる場面はあまりないと思います。
なので配列コピーする場合はディープコピーを使うのが安全です。