📠

Objectの参照・コピーについて

2021/01/21に公開

概要

Objectをコピーする際に勘違いをしていたので、備忘録として書いておく。

実行確認

http://runstant.com/supermuscles/projects/b2999826

参照渡し

Objectをそのまま別の変数に代入した場合、ただの参照渡しになる。
参照先が一緒なので、代入した先のObjectの中身を変えたら元のObjectの中身も変わってしまう。

  var base_obj = {str: 'hoge', num: 1};
  var copy_obj = base_obj; // 参照渡し
  copy_obj.num = 2; // 参照なのでbase_objの値も変わる
  console.log('base_obj', base_obj); // base_obj {"str": "hoge","num": 2}

浅いコピー(shallow copy)

Objectの第一層のみをコピーする。
もう一個深い階層についてはただの参照になってしまう。
Object.assignを使う。

  var base_obj = {str: 'hoge', num: 1, arr:[1,2,3,4,5]};
  var copy_obj = Object.assign({}, base_obj); // shallow copy
  copy_obj.str = 'fuga';
  copy_obj.arr[0] = 0; // 参照になってるのでbase_objのarrの値も変わってしまう
  console.log('base_obj', base_obj); // base_obj {"str": "hoge","num": 1,"arr": [0,2,3,4,5]
}

深いコピー(deep copy)

参照を渡すのではなく、Objectの中身の値をコピーする。
JSONオブジェクトに変換(JSON.stringify)したものをObjectにまた戻す作業(JSON.parse)をしてから、変数に代入している。

  var base_obj = {str: 'hoge', num: 1, arr:[1,2,3,4,5]};
  var copy_obj = JSON.parse(JSON.stringify(base_obj)); // deep copy
  copy_obj.str = 'fuga';
  copy_obj.arr[0] = 0;
  console.log('base_obj', base_obj); //base_obj {"str": "hoge","num": 1,"arr": [1,2,3,4,5]
}

まとめ

自分が実務で使っている限りはあまりコピー元を変更したくないパターンが多いので、上記の方法のdeep copyを使うが
配列のコピーであればスプレッド構文([...arr])で実装することもある。

そもそも同じような情報をいろんな場所で管理すること自体が煩雑化の原因になる気がするので
本当にコピーする必要があるのかは考えた方が良いのかも知れない。

Discussion