When copying nested objects in JavaScript, shallow copies like Object.assign() or the spread operator ({ ...obj }) only copy top-level properties by value, leaving inner objects referenced by pointer.
The Modern Solution: structuredClone
Modern JavaScript environments (Node.js 17+ and all modern browsers) include a native global method: structuredClone().
const original = {
name: "DevFiddle",
tags: ["tools", "snippets"],
metadata: {
created: new Date(),
id: 101
}
};
// Native deep clone in modern JS:
const clone = structuredClone(original);
clone.metadata.id = 999;
console.log(original.metadata.id); // 101 (completely independent!)Recursive Fallback for Older Environments
If your environment or build pipeline targets older runtimes, use this robust recursive cloner that supports nested arrays, plain objects, and dates:
function deepClone(value, hash = new WeakMap()) {
// Primitives and functions pass by value or reference
if (Object(value) !== value || typeof value === "function") {
return value;
}
// Handle circular references
if (hash.has(value)) {
return hash.get(value);
}
// Handle Dates & RegExp
if (value instanceof Date) return new Date(value);
if (value instanceof RegExp) return new RegExp(value.source, value.flags);
// Handle Arrays & Objects
const result = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
hash.set(value, result);
for (const key of Reflect.ownKeys(value)) {
result[key] = deepClone(value[key], hash);
}
return result;
}Why Avoid JSON.parse(JSON.stringify(obj))?
While JSON.parse(JSON.stringify(obj)) is a common shortcut, it has major limitations:
- Converts
Dateobjects into ISO strings. - Discards
undefined,Symbol, and function properties. - Converts
NaNandInfinityintonull. - Throws an error on circular object references.