← JavaScript

Arrays & Objects

What is the difference between a shallow copy and a deep copy?

Show answer — try answering out loud first

A shallow copy only copies the top level; nested objects or arrays still share the same reference. A deep copy copies everything, including the nested levels, creating fully independent objects.

When you copy an object with spread ({...obj}) or Object.assign, you get a shallow copy: the top-level properties are new, but if there's another object or array inside, that one stays the same (they share a reference). Modifying the nested object in the copy also affects the original.

A deep copy clones everything, all the way down, so the original and the copy are completely separate.

Ways to make a deep copy:

  • structuredClone(obj) — the modern, recommended approach.
  • JSON.parse(JSON.stringify(obj)) — it works, but it loses functions and undefined, turns dates into strings, etc.
// Shallow copy: the nested level is shared
const original = {
  nombre: "Mike",
  direccion: { ciudad: "Ciudad de México" },
};

const superficial = { ...original };
superficial.nombre = "Alguien";              // only affects the copy
superficial.direccion.ciudad = "Puebla";     // affects BOTH

console.log(original.nombre);                 // "Mike" (independent)
console.log(original.direccion.ciudad);       // "Puebla" (the reference was shared)

// Deep copy with structuredClone
const original2 = { direccion: { ciudad: "Ciudad de México" } };
const profunda = structuredClone(original2);
profunda.direccion.ciudad = "Monterrey";

console.log(original2.direccion.ciudad); // "Ciudad de México" (intact)
console.log(profunda.direccion.ciudad);  // "Monterrey"

Thinking that {...obj} or Object.assign make a full copy. They only copy the top level. If the object has nested data, modifying it in the "copy" also changes the original, which causes bugs that are very hard to track down.

"A shallow copy, like the one I make with the spread operator, only copies the top level; nested objects or arrays still point to the same reference, so modifying them affects the original. A deep copy clones everything all the way down and leaves the objects completely independent. For a deep copy I use structuredClone, which is the modern approach; JSON.parse(JSON.stringify()) used to be used, but that loses functions and turns dates into strings."

Quick challenge

What does this print?

const a = { lista: [1, 2] };
const b = { ...a };
b.lista.push(3);
console.log(a.lista);
See answer

It prints [1, 2, 3]. The spread makes a shallow copy, so a.lista and b.lista are the same array. Calling push on b.lista also changes the one on a.