Assigning an object to another variable does not clone it:
1 | const original = { name: 'Hudson' }; |
Both variables reference the same object. The variables are separate; the object is not.
Shallow copies
Spread syntax and Object.assign create a new outer object:
1 | const original = { |
Both are shallow copies. Nested objects remain shared:
1 | withSpread.location.city = 'Nam Dinh'; |
Arrays behave the same way:
1 | const copy = [...originalArray]; |
The array container is new, but nested objects inside it are still shared.
Deep cloning with structuredClone
For supported data types, use the platform API:
1 | const clone = structuredClone(original); |
structuredClone handles circular references and many built-in types such as Date, Map, Set, ArrayBuffer, and typed arrays. It does not clone functions, DOM nodes, or every custom runtime object.
Why JSON cloning is not general-purpose cloning
1 | const clone = JSON.parse(JSON.stringify(value)); |
This is a serialization round trip, not a complete clone algorithm. It loses or changes values such as:
undefined, functions, and symbols;Dateobjects, which become strings;MapandSet;NaNandInfinity;- circular references, which throw an error.
Use it only when the value is intentionally JSON-compatible.
Often, do not clone at all
Deep cloning a large object can be expensive and may hide unclear ownership. Prefer immutable updates that copy only the path being changed:
1 | const updated = { |
The real question is not “Which cloning trick is fastest?” It is “Which parts of this data should be shared, and which parts should have independent ownership?” Once that is clear, the code usually becomes boring—which is excellent news for state management.