JavaScriptで連想配列(オブジェクト)のコピーをする正しい方法

JavaScriptでよく使うデータ型と言えば連想配列(あるいはオブジェクトとも言う)

これをコピーする場合、間違った方法を使うとバグや不具合の原因になります。しかもエラーも出ないので自分でミスにも気づきにくいのが厄介な所です。

ここでは連想配列をコピーする本当に正しい方法とコード例についてまとめました。

連想配列コピーのNG例

まず連想配列コピーでやってはいけないNG例を紹介します。

それはある変数に連想配列をそのまま代入してしまうこと

これをやってしまうとコピー元とコピー先のデータが共有されてしまいます。

 

例えば次のように連想配列をコピーした(つもり)のコード例で考えてみましょう。

連想配列のコピー元が original  、コピー先が cloned  です。

コードとしては間違ってはいないですが、データそのものをコピーする場合は正しくありません。

 

どういう風に間違っているかは次のコードを実行してみれば分かるはず

コピー先の連想配列の値を変更した後 original  と cloned  をコンソール表示するコード例です。

表示結果はそれぞれ次の通り・・・

まず次がコピー元 original  のコンソール表示、

そして次がコピー先 cloned  のコンソール表示

・・・見てわかるように両方とも同じ内容に書き換わってしまっています。

 

こうなる理由は連想配列のコピーのされ方に次の2種類があるため

  • 浅い(シャロー)コピー【Shallow Copy】
  • 深い(ディープ)コピー【Deep Copy】

浅いコピーの場合はオブジェクトの参照(データの置き場所)が共有されてしまうのに対し、深いコピーの場合は内部のデータそのものまで全部コピーされます。

コード例のようにただ代入でコピーしてしまうと浅いコピーが起きてしまうので、一方を変更すると両方に変更が反映されてしまうという訳です。

浅いコピーは階層を持たない配列とか数値とかのコピーを表すのに対し、深いコピーはオブジェクト内にオブジェクトの入ってるデータや2階層以上の階層を持つオブジェクトのコピーを表す訳です。

※ コメント欄で間違いを指摘してくださった方、ありがとうございます m(__)m

なので完全に連想配列をコピーするにはディープコピーする必要があります。

連想配列をディープコピーするには

ではどうやって連想配列をディープコピーするのか・・・ということですがやり方は簡単

それは Object.create  という連想配列専用のメソッドを使うことです。

例えばコピー元を original  とすれば次のように書けばOK

これで cloned  が original  のディープコピーになります。

 

例えば次が Object.create  を使って連想配列をコピーしているコード例

先ほどのコードと同じく original  と cloned  をコンソール表示しています。

このコードを実行したときの結果はそれぞれ次の通り・・・

まず次がコピー元の連想配列 original  のコンソール表示、

そして次がコピー先の連想配列 cloned  のコンソール表示

・・・今度は無事 cloned  の変更が original  に影響を与えないようになりました。

 

以上が連想配列をディープコピーする方法です。

浅いコピーは意図的でない限りほとんど使わないので基本的にはディープコピーした方がバグや不具合の温床になりにくいと思います。

配列をコピーする場合もディープコピーが必要

ここまでで連想配列をコピーする方法についてまとめてきました。

コピー種類にはシャローとディープの2種類があり、データも含めて完全にコピーするにはディープコピーが必要なことを忘れないようにしたいですね。

 

ちなみに配列を完全コピーする場合もディープコピーが必要になります。

そのやり方やコード例は次でまとめた通り

連想配列と同じく配列コピーする場合もコピー方法には要注意です。

ここまでのまとめ

ということで連想配列のコピー方法まとめ

  • 代入でコピーすると浅いコピーになる
    連想配列を別屁数に直接代入すると浅いコピー(Shallow Copy)になってしまう。
    参照が同じなので一方を変更するともう一方も変更されてしまう点に要注意!
  • Object.createを使えば深いコピーになる
    連想配列内のデータも含め全部コピーするには Object.create  を使うことが必要
    参照が別々になるので一方が変更されてももう一方に影響を及ぼさない

場合によっては意図的に浅いコピーが必要になる場面もあるかもしれません。

しかし基本的には Object.create  を使うのが確実で安全な方法だと思います。

以上JavaScriptで連想配列をコピーする方法についてでした。

Commentsこの記事についたコメント

4件のコメント
  • satoyama より:

    JSでオブジェクトのことを連想配列とは呼ばないでほしいです。

    • ぴー助 より:

      コメントありがとうございます!

      確かにJavaScriptのオブジェクトは連想配列と呼ぶには少し不適切かもしれません・・・
      ですが他言語(たとえばPHPなど)での連想配列と同じ使い方ができるという意味で “連想配列” と呼んでいます。またGoogleサジェスト的には「javascript 連想配列 コピー」などのように “連想配列” という検索が一般的なので、それに合わせています。

  • satoyama より:

    あと、シャローコピーとディープコピーの意味も全て間違ってますね。
    修正したほうがいいと思います。

    参照を変数に入れることをシャローコピーというのではなく
    一段回のコピーをシャローコピーといいます。

    ディープコピーは、オブジェクトの中にオブジェクトが入っているもの
    あるいは、3段階も4段階も中にオブジェクトが入っているものをコピーすることを言います。

    • ぴー助 より:

      これについては勉強不足でした。

      確かに次のページなどを見ると、このように書いてあります。

      ===============================

      シャローコピー(浅いコピー)はプリミティブ値(文字列、数値、真偽値、null、undefined、Symbol)をコピーするが、それ以外のオブジェクトは参照をコピーする。参照がコピーされるということは、コピー元とコピー先でオブジェクトが共有されるということである。
      一方、ディープコピー(深いコピー)はプリミティブ値だけでなく、オブジェクトも値としてコピーする。したがって、コピー元とコピー先のオブジェクトは別物である。

      参照元 : https://nansystem.com/shallow-copy-vs-deep-copy/

      ==============================

      また海外のサイトでも、このように説明されています。

      ==============================

      An object is said to be shallow copied when the source top-level properties are copied without any reference and there exists a source property whose value is an object and is copied as a reference.

      A deep copy will duplicate every object it encounters. The copy and the original object will not share anything, so it will be a copy of the original.

      参照元 : https://medium.com/@arunrajeevan/shallow-copy-vs-deep-copy-in-javascript-5ce718725a0

      ==============================

      あいまいな理解で記事を書いてしまったので、いつか記事修正したいと思います。
      間違いを指摘していただき、本当にありがとうございます。m(__)m

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください