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.
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.
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.
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.
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") andsocial
is an object with propertyX
. 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).
- Top-level properties like
shallowCopy.name = "Learnify";
only changesshallowCopy.name
.original.name
is not affected because strings are primitive values copied by value.shallowCopy.social.X = "@learnify";
does affectoriginal.social.X
. Because bothoriginal.social
andshallowCopy.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") andsocial
is an object with propertyX
. structuredClone()
creates a deep copy of the original object. This means all nested objects (likesocial
) are also cloned, not just referenced.- So,
deepCopy.social
is a completely new object, separate fromoriginal.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.