Deep vs Shallow Copy: JSON.parse と stringify の落とし穴

2025-03-19

元データを壊さずに編集したいとき、JSON の往復は手軽です。ただし純粋な JSON 以外の値が混ざると、値消失や例外がすぐ発生します。

Shallow copy と deep copy

スプレッドや Object.assign は 1 階層のみ複製し、ネストは共有参照のままです。

JSON 往復は JSON で表現できる値だけを保持します。

何が失われるか

undefined、関数、Symbol キーは落ち、Date は文字列になります。

Map/Set/RegExp は期待通り残らず、NaN/Infinity も問題を起こします。

const o = { d: new Date(), fn: () => 1, u: undefined };
JSON.parse(JSON.stringify(o)); // { d: "2026-06-10T..." }

循環参照は例外

obj.self = obj のような構造は Converting circular structure to JSON を発生させます。

グラフ構造は JSON ではなく structuredClone や WeakMap 走査を使います。

structuredClone が有利な場面

モダンブラウザと Node 17+ で Date/Map/Set/循環参照を保持できます。

関数や DOM ノード、独自プロトタイプの振る舞いは維持されません。

使い分けの指針

API レスポンスのような純 JSON には JSON 往復が軽量です。

Date や循環参照を含む場合は structuredClone、複雑なクラス構造は cloneDeep を検討します。