💨

Javascriptのシャローコピー/ディープコピーについて

に公開

📝 はじめに

最近「りあクト TypeScriptで始めるつらくないReact開発」の第5版が発売されたので読んでいるのですが、3巻あるうちの① 言語環境編を読んでいるだけで今までなんとなくで使っていたことについてなぜこうなるのか、内部でどういう挙動になっているのかなどの説明がわかりやすく丁寧に書いてあるのでとても勉強になっています。
今回は、シャローコピー (Shallow Copy)ディープコピー (Deep Copy) の記事です。
破壊的変更を避けるための代替手段として使われるメソッドの説明で、「これはシャローコピーです」と書いてあってもいまいちピンときていなかったのですが、りあクトで整理できたので記事にしました。

🔍 1. シャローコピーとディープコピーの違い

2つの違いは下記のようになっています。

  • シャローコピー: 1 階層目のみ複製。ネストしたオブジェクト/配列は 参照が共有 される。 (ネストは共有)
  • ディープコピー: すべての階層を再帰的に複製。循環参照も保持。 (完全分離)

🛠 2. オブジェクトをコピーするための書き方

2-1. Object.assign

const original = { a: 1, b: 2 };
const copy     = Object.assign({}, original);

console.log(copy);             // { a: 1, b: 2 }
console.log(copy === original); // false
  • 破壊的: 第 1 引数 (ターゲット) が直接書き換わるので注意。

2-2. スプレッド構文

const original = { a: 1, b: 2 };
const copy     = { ...original };
  • 非破壊的・記法が短いので現在はこちらが主流。

2-3 JSON.stringify()

const original = { a: 1, b: 2 };
const copy     = JSON.parse(JSON.stringify(original));
  • 力技
  • プロパティに Date オブジェクトや関数、undefined とかが入 ってた場合はデータが壊れてしまう。

⚠️ 3. シャローコピーでハマる例

const alice = {
  name: 'Alice',
  address: { city: 'Tokyo' },
};
const bob = { ...alice, name: 'Bob' };

bob.address.city = 'Osaka';

console.log(alice.address.city); // ❗ 'Osaka' に変わってしまう
  • address オブジェクトの 参照が共有 されているため、片方を更新するともう片方も変化する。

🤿 4. ディープコピーの手法と比較

方法 特徴 注意点
JSON.stringifyJSON.parse 手軽・ブラウザ/古め Node でも動く Date, Map, Set, undefined, 関数 が壊れる
structuredClone(推奨) ネイティブ・広範な型対応・循環参照 OK IE など超レガシー環境ではポリフィル要
lodash.cloneDeep 実績豊富 追加依存・バンドルサイズ増

4-1. structuredClone の例

const alice = {
  name: 'Alice',
  address: { city: 'Tokyo' },
};

const bob = structuredClone(alice);
bob.address.city = 'Osaka';

console.log(alice.address.city); // 'Tokyo' のまま
  • Node.js v17 / Chrome 98 以降などで標準搭載。
  • Blob, File, Map, Set, Error など多くのビルトイン型をサポート。

🎯 5. いつディープコピーが必要か?

  • 不変データ (immutability) を保ちたい状態管理 (Redux, Recoil など)
  • Undo / Redo 機能で完全なスナップショットを保存したい場合
  • Web Worker など 構造化複製アルゴリズム が期待される場面

💡 6. まとめ

  1. まずシャローコピーで十分か確認
    • トップレベルのみ変更・読み取り専用ならシャローで OK。
  2. 深いネストや循環参照があるなら structuredClone
    • 非対応環境ではポリフィル or cloneDeep
  3. 巨大データはコピーより参照共有+パッチ適用を検討
    • Immer.js などのライブラリが便利...らしい。

もっと早くこの書籍読んでいればよかったと思うほどにめちゃくちゃ良書でした。
2、3巻読むのが楽しみです。

🔗 参考リンク

Discussion