【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

HTML/CSS/JavaScript
スポンサーリンク
この記事を書いた人
penpen

1991生まれ。
2019年くらいからフロントエンドエンジニアを目指している元アフィリエイターです💩

penpenをフォローする
penpenをフォローする
penpenメモ

コメント

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