$ snippet deep-clone
Deep clone an object
structuredClone vs the JSON trick vs a recursive clone — what each one silently drops.
/** Best default: handles Date, Map, Set, RegExp, nested arrays, cycles. */
function deepClone(value) {
return structuredClone(value);
}
/** Fallback for very old environments, or when you need to skip functions. */
function deepCloneManual(value, seen = new WeakMap()) {
if (value === null || typeof value !== "object") return value;
if (seen.has(value)) return seen.get(value);
if (value instanceof Date) return new Date(value);
if (value instanceof RegExp) return new RegExp(value.source, value.flags);
if (value instanceof Map) { const m = new Map(); seen.set(value, m); value.forEach((v, k) => m.set(deepCloneManual(k, seen), deepCloneManual(v, seen))); return m; }
if (value instanceof Set) { const s = new Set(); seen.set(value, s); value.forEach(v => s.add(deepCloneManual(v, seen))); return s; }
const out = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
seen.set(value, out);
for (const key of Reflect.ownKeys(value)) out[key] = deepCloneManual(value[key], seen);
return out;
}How it works
structuredClone is built into every modern browser and Node 17+, and it is the right answer almost always. The old JSON.parse(JSON.stringify(x)) idiom turns Date into a string, drops undefined, functions, Map and Set, turns NaN into null, and throws on circular references.
Gotchas
structuredClonethrows on functions, DOM nodes and class instances with methods (it keeps own data, loses the prototype). Use the manual version if you need prototypes preserved.- Spread
{...obj}andObject.assignare shallow: nested objects are still shared. - For immutable-state updates (React, Redux) you usually want a targeted copy of the changed path, not a full deep clone on every change.
Ad slot