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.