Deep Copy and Shallow Copy

When you're coding in JavaScript, it's important to understand how data is stored and copied in memory. Interviewers love asking about it - especially using objects and arrays!


Before we get into copying data, let’s understand where data is stored.

📦 Stack vs Heap

🧱 Stack:

  • Stores simple (primitive) values like numbers, strings, boolean

  • Fast and small.

  • Stores actual values.

🏗️ Heap:

  • Stores complex values like objects and arrays.

  • Bigger and slower.

  • Stack stores a reference (pointer) to the actual object in the heap.

📦Trade Off Between these two


1️⃣ Pass by Value

Used with primitive types (number, string, boolean)

const p1 = {
    name: "John",
    age: 20
};

const p2 = {
    name: p1.name,
    age: p1.age
};

p2.name = "Gaurav";
console.log(p1.name); // "John"

✅ Here, p2.name is a copy of p1.name, not a reference. So changes to p2 do not affect p1.


2️⃣ Pass by Reference

Used with objects and arrays

const obj = {
    name: "Gaurav",
    address: {
        city: "Varanasi"
    }
};

const newObj = { ...obj }; // Looks like a copy => Shallow Copy

You might think newObj is a full copy. But...

newObj.address.city = "Delhi";
console.log(obj.address.city); // ❗ "Delhi"

😱 Why did obj also change?

🧠 Because the address object is stored in the heap, and both obj.address and newObj.address point to the same place in memory!

This is called a shallow copy.


🥘 Shallow Copy

A shallow copy copies only the top-level properties. Nested objects are still shared.

jsCopyEditconst obj = {
    name: "Gaurav",
    address: {
        city: "Varanasi"
    }
};

const newObj = { ...obj }; // shallow copy

obj.name is copied by value.
obj.address is copied by reference (same memory).

That’s why if you change newObj.address.city, it affects obj.


©️Deep Copy

A deep copy copies everything — even nested objects — into new memory.

✅ Option 1: Manual Deep Copy

const obj2 = {
    ...obj,
    address: { ...obj.address }
};

Now, obj2.address is a new object, not shared with obj.

✅ Option 2: JSON Method

const objString = JSON.stringify(obj);
const obj3 = JSON.parse(objString);

This creates a completely new object, including all nested data.

🚫 But it doesn't work well with:

  • Functions

  • undefined

  • Dates

  • Circular references


🧠 Visualizing Stack and Heap


🧠 Summary Table

ConceptApplies ToMemory UsedShared?
Pass by ValuePrimitivesStack❌ No
Pass by ReferenceObjects/ArraysStack + Heap✅ Yes
Shallow CopyObjects/ArraysStack + Heap✅ Part
Deep CopyObjects/ArraysStack + Heap❌ No

"In JavaScript, primitive values are passed by value and stored in stack, but objects and arrays are passed by reference. Spread operator gives a shallow copy — only one level deep. For true deep copies, we can use JSON methods or libraries like Lodash."


📌 Final Thoughts

  • Use spread operator when you only need a shallow copy.

  • Use manual or JSON deep copy when you want to fully separate the copy from the original.

  • Always understand what's in the heap - that’s where shared bugs come from.

10
Subscribe to my newsletter

Read articles from Gaurav Kumar Maurya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gaurav Kumar Maurya
Gaurav Kumar Maurya