JavaScript Shallow Copy vs Deep Copy: Explained for Beginners

Mandeep KaurMandeep Kaur
5 min read

When you're working with objects or arrays in JavaScript, it's important to understand the difference between shallow copy and deep copy. Without this understanding, you might unintentionally change values in places you didn’t expect.

What Does "Copy" Mean in JavaScript?

In JavaScript:

  • Primitive types (like numbers, strings, booleans) are copied by value.

      let a = 10;
      let b = a; // value is copied ie 10
    
       b = 30;
       console.log(a) // 40
       console.log(b) // 30
    

    Here, a and b are completely separate because primitives are copied by value.

  • Objects and arrays are copied by reference — meaning both variables point to the same memory location.

      const obj1 = { name: "mandeep" };
      const obj2 = obj1; // here address copied not the value 
    
      obj2.name = "Dev Simplified";
    
      console.log(obj1.name); // "Dev Simplified"
      console.log(obj2.name); // "Dev Simplified"
    

    Here, both obj1 and obj2 point to the same object. Changing one affects the other. This is because objects are copied by reference.

    How Reference Copy Works (Behind the Scenes)

    When you do:

      const obj1 = { name: "Mandeep" };
      const obj2 = obj1;
    

    Think of it like this:

    • obj1 holds the memory address of the actual object:
      👉 obj1 --> (memory address: 0x01) --> { name: "Mandeep" }

    • When you assign obj2 = obj1, you're not copying the value, you're copying the address:
      👉 obj2 --> (same address: 0x01) --> { name: "Mandeep" }

So both obj1 and obj2 now point to the same object in memory.

That’s why if you do:

    obj2.name = "Dev Simplified";

The change reflects in both obj1 and obj2, because they're pointing to the same memory location.

What is a Shallow Copy?

A shallow copy means the top-level properties are copied, but nested objects or arrays are still linked to the original.

When you do a shallow copy:

    const original = {
      name: "Mandeep",
      social: { twitter: "@mandeep" }
    };

    const shallow = { ...original };

Think of it like this:

    original.name  --> "Mandeep"   (copied as value)
    original.social --> (address: 0x02) --> { twitter: "@mandeep" }

    shallow.name   --> "Mandeep"   (new value copied)
    shallow.social --> (same address: 0x02) --> { twitter: "@mandeep" }

✔️ name is a primitive, so its value is copied.

⚠️ social is an object, so only the reference (address) is copied — not a full copy of the object.

So if you do:

    shallow.social.twitter = "@devsimplified";

It also updates original.social.twitter, because both point to the same nested object.

Methods to Create Shallow Copies

  • Spread operator ...

  • Object.assign()

  • slice() or concat() for arrays

What is a Deep Copy?

A deep copy duplicates everything, including all nested objects and arrays.

When you do a deep copy:

    const deep = JSON.parse(JSON.stringify(original));

It creates completely new memory for everything, including nested objects:

  • Now the nested social object is completely independent.

    Behind the Scenes of JSON.stringify()

    When you run:

      const obj = {
        name: "Mandeep",
        skills: ["JavaScript", "React"]
      };
    
      const jsonStr = JSON.stringify(obj);
    

    Here's what happens internally:

    🔄 Step-by-Step Process:

    1. Traverse the object:

      • JSON.stringify walks through each key-value pair in the object.

      • If it finds a nested object or array, it recursively walks through that too.

    2. Convert primitives to strings:

      • Numbers, strings, booleans, and null are directly converted to their string forms.
    3. Handle arrays and objects:

      • Arrays are serialized with brackets [] and each element is stringified.

      • Objects are serialized with braces {} and each key is turned into a string.

    4. Ignore or skip unsupported types:

      • Functions, undefined, Symbol, circular references, and certain objects like Map, Set, Date (partially) are either skipped, ignored, or cause errors.

After all of that, JSON.stringify() returns a flat string — a linear representation of your object:

    {
      "name": "Mandeep",
      "skills": ["JavaScript", "React"]
    }

This is now just a string — not an object. You can't access properties on it until you convert it back using JSON.parse().

JSON.parse + JSON.stringify = Deep Copy? 🤔🤔🤔🤔 —- Yes — but with gotchas.

⚠️ Limitations to Remember:

TypeBehavior
undefinedSkipped
FunctionsSkipped
SymbolSkipped
DateTurned into a string
Map, SetNot supported
Circular refs❌ Throws an error (TypeError)

So it’s great for simple, serializable data, but not for complex structures.

Better Deep Copy Methods

  • structuredClone() — built-in and supports more types

  • lodash.cloneDeep() — popular library for complex structures

When to Use What?

  • Use shallow copy for simple/flat objects

  • Use deep copy when working with nested or complex data

Final Thoughts💡

Understanding how JavaScript handles copying — whether by value or by reference — is key to writing bug-free code.

  • Use shallow copies when you're dealing with simple, flat objects.

  • Use deep copies when working with nested structures, but be cautious about the method you choose.

  • JSON.parse(JSON.stringify()) is a quick solution for deep copying plain objects, but it has limitations.

  • For more complex scenarios, prefer structuredClone() or libraries like lodash.cloneDeep().

Being intentional about how you copy data can save hours of debugging and ensure your apps behave predictably.

What’s a tricky JavaScript concept you want explained next? Drop a comment below!

0
Subscribe to my newsletter

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

Written by

Mandeep Kaur
Mandeep Kaur

Frontend Engineer 👩🏻‍💻 | Crafting seamless and responsive user interfaces with React & modern JavaScript | Passionate about clean code, UI/UX, and performance optimization | Sharing frontend tips, tricks, and tutorials