【JavaScript】ディープコピーとシャローコピーの違い

調べたことメモ。

違い

  • ディープコピー:オブジェクトの中のオブジェクトまで丸ごとコピーする
  • シャローコピー:オブジェクトの中の中のオブジェクトは参照コピーする

 

シャローは「浅い」、

ディープは「深い」という意味。

シャローコピーとは?

実例で見たほうが分かりやすいと思うので、例を出します。

 

例えば、以下のようなオブジェクトがあるとします。

const human= {
  name:"tarou",
  age:60,
  myChild:{
    name:"zirou",
    age:29
  }
}

「60歳のオッサンに29歳の息子がいる」なオブジェクトです。

 

これを以下のようにしてコピーします。

const cloneHuman = Object.assign({}, human);

// Object.assign()は、オブジェクトをコピーしたりできるメソッド。
//第一引数にコピー先のオブジェクト、第二引数にコピー元のオブジェクトを指定する。

 

すると、cloneHumanはhumanとまったく同じ「60歳のオッサンに29歳の息子がいる」なオブジェクトになります。

 

ところが、これだとcloneHumanオブジェクトのmyChildオブジェクトは参照コピーされています。

例えば、cloneHumanオブジェクトのmyChildオブジェクトのageを「111」に変えてみます。
すると、humanオブジェクトのmyChildオブジェクトのageまで「111」に変わってしまいます。

シャリーコピー
▲humanオブジェクトのほうまで変わってしまっている。

 

図解すると以下のような感じ。

シャローコピーした際の図

 

「オブジェクトの中のオブジェクトは参照コピーするようにする」というのはJavaScriptの仕様らしい。

ディープコピーとは?

オブジェクトの中にあるオブジェクトまで完全にコピーするには、自分で実装するしかないらしい。

方法①:愚直におこなう

例えば以下のようにすると、ディープコピーされます。

//シャローコピー
const shallowClone = (obj) => {
  return Object.assign({}, obj);
};

//ディープコピー(中身がオブジェクトの場合は再帰的にシャローコピーする)
function deepClone(obj) {
  const newObj = shallowClone(obj);
  // プロパティがオブジェクト型であるなら、再帰的に複製する
  Object.keys(newObj)
      .filter(k => typeof newObj[k] === "object")
      .forEach(k => newObj[k] = deepClone(newObj[k]));
  return newObj;
}


//ディープコピーする
const cloneHuman = deepClone(human);

 

図解すると以下のような感じ。

ディープコピー(javascript)

ディープコピーの場合は、humanオブジェクトとcloneHumanには何の関係もないので

例えば、cloneHumanオブジェクトのmyChildオブジェクトのageを「111」に変えてみても
humanオブジェクトのmyChildオブジェクトのageは「29」のまま変化しません。

方法②:シリアライズで簡易におこなう

単純にシリアライズ → デシリアライズという形でも、ディープコピーできるようです。

var cloneHuman = JSON.parse(JSON.stringify(human));

 

ただし、この方法の場合は以下のような欠点があるようです。

 この方法の問題は、先に述べたようなStringや配列、Numberなどの独自リテラルを持つオブジェクトにしか使えない、ということです。

例えばDateオブジェクトや、jQueryオブジェクト、BigNumber.jsなどの拡張されたオブジェクトには一切使えません。これらはDOM要素を直接、参照として持っていたりするので、ライブラリの深いところまで知り尽くしていないと軽々しくコピー出来ないわけです。

しかしながら、これは大した問題にはなりません。そういったオブジェクトをディープコピーする必要は殆どありませんし、また、そういったことが必要なライブラリにはコピーする手段が必ず用意されているからです。

引用:https://www.deep-rain.com/programming/javascript/856

方法③:ライブラリを使う

Lodashなどの外部ライブラリを使えば、一発でディープコピーできるらしい。

const cloneDeep = require('cloneDeep');
const cloneHuman = cloneDeep(human);

 

おわり

 

 

参考にしたページ:

https://jsprimer.net/basic/object/

https://www.deep-rain.com/programming/javascript/856

コメント

タイトルとURLをコピーしました