Shallow vs Deep Copy

Learn the difference between shallow and deep copies in JavaScript with simple examples. Understand how copying objects affects nested data.

Loading...
Shallow vs Deep Copy

Before diving into copies, a quick recap of data types.

JavaScript has two main kinds of data types:

  • Primitive data types: Like numbers, strings, and booleans.
  • Reference data types: Like arrays and objects.

Learn more about data types!

Primitive data types are copied by value. This means a new independent copy is made.

Reference data types are copied by reference. That means if you assign an object to another variable, you're not copying it, you're just pointing to the same thing.

For example:

const original = { name: "Shefali" };
const copy = original;
 
copy.name = "Learnify";
 
console.log(original.name); // Output: "Learnify"

Here, even though you changed name in copy, it also affected name in original, because both refer to the same object.

Learn more about memory and references!

What is a Shallow and Deep Copy?

A shallow copy means copying the top-level properties of an object. If the object has nested objects or arrays, those are still shared with the original. That means if you make changes in the original object or array, those changes will also affect the copied object or array.

A deep copy copies everything, including nested objects or arrays. The new copy is completely independent from the original. That means if you make changes in the original object or array, those changes will not affect the copied object or array.

To put it simply, imagine you and your friend each have your own plate, but there’s only one pizza placed between you. You both take slices from that same pizza.

So if your friend eats a slice, it's gone for you too, because you're both using the same shared pizza, just with different plates.

JavaScript Shallow Copy

Now, imagine you both get your own pizza on separate plates.

If your friend eats a slice, it doesn't affect your pizza at all, because you each have a complete, independent copy.

JavaScript Deep Copy

In summary,

  • Shallow copy = same pizza, separate plates
  • Deep copy = two completely separate pizzas

Examples of Shallow Copy

Using the Spread Operator

const original = {
  name: "Shefali",
  social: {
    X: "@Shefali__J",
  },
};
 
const shallowCopy = { ...original };
 
shallowCopy.name = "Learnify";
shallowCopy.social.X = "@learnify"; // Affects original too!
 
console.log(original.name); // Output: Shefali
console.log(original.social.X); // Output: @learnify
console.log(shallowCopy.name); // Output: Learnify

Here,

  • In original, name is a string ("Shefali") and social is an object with property X.
  • const shallowCopy = { ...original }; uses the spread operator (...) to copy original into shallowCopy.
  • This is a shallow copy, which means,
    • Top-level properties like name are copied by value (primitives).
    • Nested objects like social are copied by reference (same memory location).
  • shallowCopy.name = "Learnify"; only changes shallowCopy.name. original.name is not affected because strings are primitive values copied by value.
  • shallowCopy.social.X = "@learnify"; does affect original.social.X. Because both original.social and shallowCopy.social refer to the same object in memory.

Using Object.assign()

const original = {
  name: "Shefali",
  social: {
    X: "@Shefali__J",
  },
};
 
const shallowCopy = Object.assign({}, original);
 
shallowCopy.name = "Learnify";
shallowCopy.social.X = "@learnify"; // Affects original too
 
console.log(original.name); // Output: Shefali
console.log(original.social.X); // Output: @learnify
console.log(shallowCopy.name); // Output: Learnify

Both methods copy only the outer object. Nested objects are still linked.


Examples of Deep Copy

Using structuredClone()

const original = {
  name: "Shefali",
  social: {
    X: "@Shefali__J",
  },
};
 
const deepCopy = structuredClone(original);
 
deepCopy.social.X = "@learnify"; // Does not affect the original
 
console.log(original.social.X); // Output: @Shefali__J
console.log(deepCopy.social.X); // Output: @learnify

Here,

  • In original, name is a string ("Shefali") and social is an object with property X.
  • structuredClone() creates a deep copy of the original object. This means all nested objects (like social) are also cloned, not just referenced.
  • So, deepCopy.social is a completely new object, separate from original.social.
  • deepCopy.social.X = "@learnify"; only changes the copy and doesn't affect the original.

Using JSON.parse(JSON.stringify(...))

const original = {
  name: "Shefali",
  social: {
    X: "@Shefali__J",
  },
};
 
const deepCopy = JSON.parse(JSON.stringify(original));
 
deepCopy.social.X = "@learnify"; // Does not affect the original
 
console.log(original.social.X); // Output: @Shefali__J
console.log(deepCopy.social.X); // Output: @learnify

Limitations of JSON.parse(JSON.stringify(...)):

  • Doesn’t work with things like functions, undefined, Symbol, Date, Map, Set, etc.
  • Ignores values it can’t copy, without giving any error.

When to Use What?

  • Use shallow copy when your data is simple and flat.
  • Use deep copy when you have nested structures and want true independence.
  • Avoid JSON.stringify/parse() unless you’re sure your data is simple and serializable.
  • Prefer structuredClone() for safe deep copies in modern apps.

Support my work!