JavaScript Shallow Copy vs Deep Copy: Explained for Beginners


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
andb
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
andobj2
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()
orconcat()
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:
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.
Convert primitives to strings:
- Numbers, strings, booleans, and
null
are directly converted to their string forms.
- Numbers, strings, booleans, and
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.
Ignore or skip unsupported types:
- Functions,
undefined
,Symbol
, circular references, and certain objects likeMap
,Set
,Date
(partially) are either skipped, ignored, or cause errors.
- Functions,
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:
Type | Behavior |
undefined | Skipped |
Functions | Skipped |
Symbol | Skipped |
Date | Turned into a string |
Map , Set | Not 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 typeslodash.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 likelodash.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!
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