![]() |
The spread syntax (...
) has become one of the most convenient and used features in modern JavaScript for copying objects and arrays. However, there's an important thing to know: the spread syntax creates a shallow copy, not a deep copy. This behavior can introduce sneaky and hard-to-track bugs.
What is a Shallow Copy?
A shallow copy copies only the top level of an object or array. If there are nested objects, they are not cloned, but copied by reference.
Example: Shallow Copy with Object
const original = {
name: 'Mario',
address: {
city: 'Rome',
zip code: '00100'
}
};
const copy = { ...original };
copy.name = 'Luigi'; // OK, independent modification
copy.address.city = 'Milan'; // ⚠️ Modify the original object too!
console.log(original.address.city); // "Milan" 😱
Example: Shallow Copy with Array
const arr = [[1, 2], [3, 4]];
const copyArr = [...arr];
copyArr[0][0] = 99;
console.log(arr[0][0]); // 99 😓
Why does this happen?
With spread syntax:
- Primitive values (strings, numbers, Booleans, etc.) are copied by value.
- Objects (including arrays) are copied by reference.
This means that changes to nested properties of the copy still affect the original.
Solutions: How to Avoid Shallow Copy Problems
1. Manual Deep Copy (Recursive)
For simple objects, you can write a recursive function:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
const result = {};
for (const key in obj) {
result[key] = deepClone(obj[key]);
}
return result;
}
const clone = deepClone(original);
✔️ Pros: Customizable
❌ Cons: Doesn't handle special types like Date
, Map
, Set
, functions, etc.
2. Use structuredClone
(native) - recommended ✅
Starting with ECMAScript 2021 (supported in modern browsers and Node.js ≥ 17), the following method is available:
const deepcopy = structuredClone(original);
✔️ Pros: secure, deep, handles manydata types (Date
, Map
, etc.)
❌ Cons: Does not support functions or objects with circular references in legacy environments.
3. JSON.parse(JSON.stringify(...))
A popular technique (but with limitations):
const copy = JSON.parse(JSON.stringify(original));
✔️ Pros: Simple, effective for JSON objects
❌ Cons: Loses methods, undefined
, Date
, Map
, functions, and causes errors on circular references.
4. External Libraries (e.g., Lodash)
Lodash's _.cloneDeep()
function is one of the most robust:
import cloneDeep from 'lodash/cloneDeep';
const copy = cloneDeep(original);
✔️ Pros: Handles almost all cases, including circular references
❌ Cons: External dependency, additional overhead
When is it okay to use Spread Syntax?
Use ...
only when:
- The object has no nested properties or
- You are aware of and intend to preserve internal references
Safe example:
const person = { name: 'Luca', age: 30 };
const copy = { ...person };
copy.name = 'Anna'; // ✅ Safe, isolated effect
Conclusion
Spread syntax is powerful and convenient, but it only creates shallow copy. If your object has nested structures, use them with caution or prefer one of the deep copy techniques above.
In summary:
Method | Copy Type | Pros | Cons |
---|---|---|---|
... |
Shallow | Fast, readable | Risk aliasing |
deepClone() |
Deep (manual) | Customizable | Limited |
structuredClone() |
Deep | Native, safe | Support to check |
JSON.stringify / parse() |
Deep | Easy | Type loss |
_.cloneDeep() (Lodash) |
Deep | Robust | External dependency |
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment