Shallow Copy vs Deep Copy in JavaScript – A Beginner-Friendly Guide

Yash GroverYash Grover
5 min read

When working with objects and arrays in JavaScript, understanding how copying works is essential to avoid bugs and unexpected behavior. This article breaks down shallow copy vs deep copy in the simplest way possible, with examples, analogies, React use cases, and answers to frequently asked questions.


🟡 What is a Shallow Copy?

A shallow copy means copying only the top-level properties of an object or array. If the object has nested values (like another object or array), the copy still points to the original nested values.

📦 Example:

const original = {
  name: "Yash",
  address: { city: "Delhi" }
};

const copy = { ...original }; // Shallow copy using spread operator
copy.address.city = "Mumbai"; // Modifying nested property in the copy

console.log(original.address.city); // 👉 'Mumbai' (original affected)

Here, copy and original both share the same address object reference. So, changing the city in copy.address also affects original.address.


🔵 What is a Deep Copy?

A deep copy creates a brand-new object and recursively copies all nested values, so the new object is fully independent of the original.

📦 Example:

const original = {
  name: "Yash",
  address: { city: "Delhi" }
};

const deep = JSON.parse(JSON.stringify(original)); // Deep copy via JSON method
deep.address.city = "Mumbai"; // Modifying nested property in deep copy

console.log(original.address.city); // 👉 'Delhi' (original untouched)

This approach ensures that changes to the copied object do not affect the original, but note that it has limitations (discussed later in this blog).


🧾 Analogy: Copying a Notebook

Imagine you have a notebook with pages and photos:

  • Shallow Copy = You photocopy a notebook with notes and diagrams in it, but you only photocopy the pages with notes and put stickers saying “see original diagrams from the book” where the diagrams were.

  • Deep Copy = You rewrite every page and recopy the diagrams as well. Now it’s a full, standalone copy.

Changing anything in the shallow copy’s diagrams affects the original notebook (since they are reference to the original one). In deep copy, both notebooks are completely independent.


🔧 All the Ways to Shallow Copy in JavaScript

Objects:

const copy = { ...original }; // Using spread operator
const copy = Object.assign({}, original); // Using Object.assign

Both methods create a new object and copy top-level properties. ( nested properties still refer the original memory address ).

Arrays:

const arrCopy = [...array];       // spread operator
const arrCopy = array.slice();    // slice method
const arrCopy = Array.from(array); // Array.from method

These methods create new arrays, but do not deeply clone nested elements.


🔧 All the Ways to Deep Copy in JavaScript

1. JSON.parse(JSON.stringify())

const deepCopy = JSON.parse(JSON.stringify(obj));

✅ Easy to use and works for plain objects.
❌ Fails when the object contains:

  • undefined

  • functions

  • Date, Map, Set

  • Circular references

2. structuredClone() (modern method)

const deepCopy = structuredClone(obj);

✅ Handles complex types like Date, Map, Set, and supports circular references.
❌ Not supported in Internet Explorer and some older environments.

3. Lodash's cloneDeep()

import cloneDeep from 'lodash/cloneDeep';
const deepCopy = cloneDeep(obj);

✅ Safe, widely used, and handles nearly every edge case including deeply nested structures and circular references.


🔄 React: Shallow Copy vs Deep Copy in State Management

Initial State:

const [user, setUser] = useState({
  name: "Yash",
  address: { city: "Delhi" }
});

This state includes a nested address object.

❌ Shallow Copy (Buggy)

const updateCity = () => {
  const updatedUser = { ...user }; // Shallow copy of user
  updatedUser.address.city = "Mumbai"; // Directly mutating nested object
  setUser(updatedUser); // React may not re-render
};

Since updatedUser.address is still a reference to user.address, this mutation can lead to bugs and stale renders. ( also now the original address’s city is changed to Mumbai ).

✅ Deep Copy (Safe)

const updateCity = () => {
  const updatedUser = {
    ...user,
    address: { ...user.address, city: "Mumbai" } // Creating a new nested object
  };
  setUser(updatedUser); // React sees new reference, triggers re-render
};

This method ensures immutability at the level you need. ( since we created a new nested object, the original one won’t be mutated ).

✅ Using cloneDeep for deeply nested structures

import cloneDeep from "lodash/cloneDeep";
const updateCity = () => {
  const updatedUser = cloneDeep(user); // Full deep copy
  updatedUser.address.city = "Mumbai";
  setUser(updatedUser);
};

Lodash's cloneDeep ensures that all levels are independently copied. ( best for deep copying stuff )


❓ Frequently Asked Questions (FAQ)

1. Why does my original object change when I modify the copy?

Because you made a shallow copy, and the nested values are still shared.

2. How can I check if a copy is shallow or deep?

Try modifying a nested property in the copy and see if it affects the original.

3. What is a circular reference?

A circular reference is when an object refers to itself directly or indirectly.

const obj = {};
obj.self = obj; // Circular reference

This breaks JSON.stringify() because it causes infinite recursion.

4. Is structuredClone() safe for deep copying?

Yes, it's a modern built-in method that handles deep cloning of most types.

5. Can I write my own deep copy function?

Yes, but it’s tricky. You need to handle all data types and circular references. It's better to use a library like Lodash.

6. Is spread operator a deep copy?

No, it only creates a shallow copy.

7. Does React do deep copying of state?

No. React doesn’t clone state for you. You are responsible for ensuring immutability.

8. Should I always use deep copy?

Not necessarily. Use shallow copy for flat data. Use deep copy when dealing with nested or complex structures that need isolation.

9. What’s the difference between deep copy and deep freeze?

A deep copy creates a new object, fully detached. A deep freeze makes an object and all its nested values immutable, preventing any changes.


✅ Final Thoughts

Understanding the difference between shallow and deep copying is crucial for writing bug-free JavaScript, especially when dealing with nested objects and arrays — like in React state. Always pick the right type of copy based on how deep your data goes.

0
Subscribe to my newsletter

Read articles from Yash Grover directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Yash Grover
Yash Grover