The built-in structuredClone()
const original = { user: { name: "Ada" }, tags: ["a", "b"] };
const copy = structuredClone(original);
copy.user.name = "Sam";
console.log(original.user.name); // "Ada"
The nested user object is fully duplicated, so editing the copy leaves the original untouched.
Why the spread operator is not enough
const original = { user: { name: "Ada" } };
const shallow = { ...original };
shallow.user.name = "Sam";
console.log(original.user.name); // "Sam" — changed!
Spread and Object.assign only copy the top level; nested objects are still shared by reference, which causes surprising bugs.
The JSON trick (for simple data)
const copy = JSON.parse(JSON.stringify(original));
This deep-clones plain data well, but it drops functions, undefined, and Date objects, so prefer structuredClone when it is available.
Which method should you use?
- structuredClone() — the best default for a true deep copy.
- JSON.parse(JSON.stringify()) — a fallback for plain JSON-safe data only.
- spread / Object.assign — fine when you only need a shallow copy.
- Common mistake: using spread and assuming nested objects were copied.
Frequently asked questions
What is the difference between shallow and deep cloning?
A shallow clone copies the top level but shares nested objects by reference. A deep clone duplicates everything, so the copy is fully independent.
Does structuredClone copy functions?
No. It cannot clone functions or DOM nodes and will throw if the object contains them. For those cases, copy the data and reattach behaviour manually.
Want to understand references and copies properly? Our free JavaScript course explains them clearly.