調べたことメモ。
違い
- ディープコピー:オブジェクトの中のオブジェクトまで丸ごとコピーする
- シャローコピー:オブジェクトの中の中のオブジェクトは参照コピーする
シャローは「浅い」、
ディープは「深い」という意味。
シャローコピーとは?
実例で見たほうが分かりやすいと思うので、例を出します。
例えば、以下のようなオブジェクトがあるとします。
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);
図解すると以下のような感じ。
ディープコピーの場合は、humanオブジェクトとcloneHumanには何の関係もないので
例えば、cloneHumanオブジェクトのmyChildオブジェクトのageを「111」に変えてみても
humanオブジェクトのmyChildオブジェクトのageは「29」のまま変化しません。
方法②:シリアライズで簡易におこなう
単純にシリアライズ → デシリアライズという形でも、ディープコピーできるようです。
var cloneHuman = JSON.parse(JSON.stringify(human));
ただし、この方法の場合は以下のような欠点があるようです。
この方法の問題は、先に述べたようなStringや配列、Numberなどの独自リテラルを持つオブジェクトにしか使えない、ということです。
例えばDateオブジェクトや、jQueryオブジェクト、BigNumber.jsなどの拡張されたオブジェクトには一切使えません。これらはDOM要素を直接、参照として持っていたりするので、ライブラリの深いところまで知り尽くしていないと軽々しくコピー出来ないわけです。
しかしながら、これは大した問題にはなりません。そういったオブジェクトをディープコピーする必要は殆どありませんし、また、そういったことが必要なライブラリにはコピーする手段が必ず用意されているからです。
方法③:ライブラリを使う
Lodashなどの外部ライブラリを使えば、一発でディープコピーできるらしい。
const cloneDeep = require('cloneDeep');
const cloneHuman = cloneDeep(human);
おわり
参考にしたページ:
コメント