概要
オブジェクトを「コピーしたつもり」なのに、なぜか元の値も変わってしまう。
この現象の正体が、**shallow copy(浅いコピー)と deep copy(深いコピー)**の違いだ。
JavaScriptでは、コピーしただけでは“すべての値”が複製されるとは限らない。
特にネスト構造を持つオブジェクトや配列を安全に扱うためには、コピーの仕組みを理解し、目的に応じた設計的選択が必要だ。
対象環境
JavaScript(ES6以降)
ブラウザ / Node.js 両対応
結論:shallow copy は“参照をコピー”するだけ
✅ shallow copy(浅いコピー)
const original = { user: { name: 'toto' } };
const copy = { ...original };
copy.user.name = 'bob';
console.log(original.user.name); // ❌ 'bob'(元データが変わる)
→ ...
は top-level のプロパティしかコピーしない
→ ネストされたオブジェクトは参照ごとコピーされる
✅ deep copy(深いコピー)
const original = { user: { name: 'toto' } };
const copy = structuredClone(original);
copy.user.name = 'bob';
console.log(original.user.name); // ✅ 'toto'
→ structuredClone()
は再帰的に中身ごとコピーする
shallow copy の代表的な方法
方法 | 説明 |
---|---|
Object.assign({}, obj) |
ES5〜 可読性やや低 |
{ ...obj } |
ES6〜 モダン記法 |
Array.slice() |
配列のshallow copy |
deep copy の安全な実装方法
✅ structuredClone(最も推奨)
const copy = structuredClone(obj);
- ✅ ネストされた配列・オブジェクト・Dateなども安全に複製
- ❌ 関数やSymbol、DOMノードなどはコピー不可
※ Chrome 98+, Node 17+(Polyfillあり)
❌ JSON方式(非推奨)
const copy = JSON.parse(JSON.stringify(obj));
- ❌ Date → 文字列になる
- ❌ undefined や関数 → 消える
- ❌ 循環参照 → エラー
✅ ライブラリを使う(例:lodash)
import cloneDeep from 'lodash/cloneDeep';
const copy = cloneDeep(obj);
- ✅ 検証済みの安全なdeep copy
- ❌ バンドルサイズが増える可能性に注意
状態管理における正しい設計
React / Vue などのフレームワークで状態管理を行う場合:
-
useState()
で更新するときは 新しいオブジェクトを生成する必要がある - 既存のstateを shallow copy して、そのまま子要素を mutate → バグの温床
// ❌ NG: mutateしてしまう
const newState = { ...state };
newState.user.name = 'bob';
// ✅ OK: ネストごとコピー
const newState = structuredClone(state);
newState.user.name = 'bob';
パフォーマンス考慮
- shallow copy → 高速、だが用途限定
- deep copy → 遅くなる可能性あり、だが安全性が高い
→ ✅ 設計時に「どこまでの変更が必要か」「元データに副作用を与えるべきか」を判断軸にする
設計判断のフロー
データがネストしている? → Yes
↓
元データに影響させたくない? → Yes
↓
→ deep copy(structuredClone or cloneDeep)
↓
データがflat or 参照可でも問題なし? → Yes
↓
→ shallow copy(スプレッド構文 / assign)
結語
shallow copy か deep copy かという選択は、ただの記法の問題ではない。
それは「オブジェクトがどこに責任を持つべきか」という設計そのものである。
- 元のオブジェクトと結びついていてもいいのか?
- 安全性と可読性、どちらを優先すべきか?
- コードベースに副作用がどこまで波及するか?
コピーの深さ=設計の深さ。
JavaScriptのコピー処理は、常に意図と責任を明示するための設計選択である。