➿ Beware of Shallow Copy with Spread Syntax in JavaScript: Examples and Solutions

  


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.

🔗 Do you like Techelopment? Check out the site for all the details!

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