Deep vs Shallow Copy: Pitfalls of JSON.parse and stringify
2025-03-19
Need a copy you can mutate without touching the original? JSON.parse(JSON.stringify(obj)) is fast for plain JSON—but Date, functions, and circular references expose its limits fast.
Shallow vs deep copy
Spread {...obj} and Object.assign only clone the first level; nested objects stay shared.
The JSON round-trip only preserves JSON-serializable values.
What gets lost?
undefined, functions, and Symbol keys drop; Date becomes an ISO string, not a Date instance.
Map, Set, and RegExp degrade; NaN/Infinity become null or break stringify.
const o = { d: new Date(), fn: () => 1, u: undefined };
JSON.parse(JSON.stringify(o)); // { d: "2026-06-10T..." }Circular references throw
obj.self = obj triggers Converting circular structure to JSON.
Use structuredClone or a WeakMap walk—not JSON—for graphs.
When structuredClone wins
Built into modern browsers and Node 17+; keeps Date, Map, Set, RegExp, and cycles.
Still no functions, DOM nodes, or prototype methods.
Choosing a strategy
Plain API/config JSON: JSON round-trip stays the lightest clone.
Dates, Maps, or cycles: structuredClone; rich class graphs: lodash.cloneDeep.