When working with complex data structures like objects and arrays in JavaScript, understanding how copies work is essential. This is especially true because JavaScript objects and arrays are reference types, meaning when we assign an object to another variable, we’re not copying the object’s data; instead, we’re copying the reference to that data. If we modify the new variable, it could unintentionally affect the original object, which is often not the intended behavior.
To avoid this, we need to create a shallow copy or a deep copy depending on your use case. In this blog, we’ll explore the differences between these two types of copies, their methods, their implications, and when to use each.
What Is a Copy in JavaScript?
In JavaScript, objects and arrays are stored as references, not values. This means when we assign an object or array to another variable, the new variable does not hold a copy of the data. It holds a reference (or memory address) to the original data. Therefore, both variables point to the same object or array in memory, and modifying one will affect the other.
const obj1 = { name: "Hridoy", roll: "CE22006" };
const obj2 = obj1; // obj2 is now a reference to obj1
obj2.name = "Karn"; // Modifying obj2 also modifies obj1
console.log(obj1.name); // Output: "Karn"
What is a Shallow Copy?
A shallow copy of an object is a new object where the top-level properties are copied by value. However, if the object contains nested objects or arrays, only the references to those objects are copied, not the actual objects themselves. This means that changes to nested objects in the copied object will also affect the original object because both the original and the copied object share the same references to the nested objects.
How Shallow Copy Works
Primitive values (e.g., numbers, strings, booleans) are copied by value, meaning they are independent of the original object.
For non-primitive values like arrays and objects, the reference to the original object is copied. This means that changes made to these nested objects or arrays in the copy will affect the original object, as they point to the same location in memory.
Shallow Copy Example
const obj1 = {
name: "Hridoy",
roll: "CE22006",
address: {
city: "Cumilla",
postalCode: 4012
}
};
// Shallow copy using Object.assign()
const obj2 = Object.assign({}, obj1);
// Modify the shallow copy
obj2.name = "Karn"; // Changes only obj2
obj2.address.city = "Dhaka"; // Affects both obj1 and obj2 (same reference for address)
console.log(obj1.name); // Output: "Hridoy"
console.log(obj1.address.city); // Output: "Dhaka" (shared reference)
In the example above:
The
name
property was copied by value, so changing it inobj2
did not affectobj1
.The
address
object was copied by reference, so modifying it inobj2
also affectedobj1
.
Methods to Create Shallow Copies
There are several ways to create a shallow copy in JavaScript. Here are the most common methods:
1. Using Object.assign()
const shallowCopy = Object.assign({}, obj);
Object.assign()
takes an empty object{}
as the first argument and copies the properties of the original object (obj
) into it.
2. Using Spread Operator (...
)
const shallowCopy = { ...obj };
- The spread operator creates a shallow copy of the object by copying all top-level properties.
3. Using Array.slice()
for Arrays
For arrays, we can use the slice()
method to create a shallow copy:
const shallowArray = arr.slice();
When to Use Shallow Copy?
A shallow copy is appropriate when:
We are dealing with flat objects that don’t contain nested structures like arrays or other objects.
We don’t need to modify nested objects or arrays independently from the original.
Limitations of Shallow Copy
- Shared References: If the object has nested objects or arrays, the shallow copy will only copy the references. This can lead to unintended side effects when modifying nested structures.
What is a Deep Copy?
A deep copy creates an entirely new object, along with independent copies of any nested objects or arrays. Unlike shallow copy, the deep copy ensures that both the original and the copied object are entirely separate entities in memory, meaning changes to one object do not affect the other.
In a deep copy:
All properties of the original object are copied, including properties that are nested or references to other objects.
The deep copy creates a new memory space for each nested object or array, thus making the original and the copied object fully independent.
How Deep Copy Works
Primitive values are copied by value, just like in a shallow copy.
Objects and arrays are recursively copied, so all nested structures are duplicated.
Changes to the deep copy will not affect the original object, and vice versa.
Deep Copy Example
const obj1 = {
name: "Hridoy",
roll: "CE22006",
address: {
city: "Cumilla",
postalCode: 4012
}
};
// Deep copy using structuredClone() (modern JS)
const obj2 = structuredClone(obj1);
// Modify the deep copy
obj2.name = "Karn"; // Changes only obj2
obj2.address.city = "Dhaka"; // Affects only obj2, not obj1
console.log(obj1.name); // Output: "Hridoy" (unchanged)
console.log(obj1.address.city); // Output: "Cumilla" (unchanged)
In the example above:
The
name
property was copied by value, so modifying it inobj2
did not affectobj1
.The
address
object was also copied deeply, meaning changes to it inobj2
did not affectobj1
.
Methods to Create Deep Copies
1. Using structuredClone()
(Modern JS)
structuredClone()
is a native JavaScript function introduced to create a deep copy of any object, including arrays, Date
, Map
, and other built-in types:
const deepCopy = structuredClone(obj);
- This method works well for most use cases and handles deep cloning of objects.
2. Using JSON.parse()
and JSON.stringify()
(Simple Objects)
For simple objects that don’t contain functions or special types like Date
or RegExp
, we can use JSON.parse()
and JSON.stringify()
:
const deepCopy = JSON.parse(JSON.stringify(obj));
Limitations: This method doesn’t work for:
Functions
undefined
Symbol
Special objects like
Date
,Map
,Set
,RegExp
3. Using lodash
Library
The popular lodash
library provides the cloneDeep()
method to create deep copies:
const deepCopy = _.cloneDeep(obj);
- This method is ideal when we need a reliable, library-backed solution for deep copying.
When to Use Deep Copy?
A deep copy is necessary when:
You need to duplicate complex data structures (objects with nested objects or arrays).
You want to avoid side effects caused by modifying shared references.
Shallow Copy vs Deep Copy: Key Differences
Feature | Shallow Copy | Deep Copy |
Copy Type | Copies top-level properties and references for nested objects. | Recursively copies all properties and nested objects. |
Memory Sharing | Shares references for nested objects/arrays. | No shared references; completely independent. |
Effect of Modification | Modifications to nested objects affect both the original and the copy. | Modifications to nested objects do not affect the original or the copy. |
Performance | Faster, as it only copies top-level properties. | Slower, as it requires deep cloning of all nested structures. |
Use Cases | Ideal for flat objects or when we only need to copy top-level properties. | Ideal for complex objects with nested structures or when independent copies are needed. |
When to Use Shallow or Deep Copy?
Shallow Copy: Use this when we’re dealing with flat objects or when we don’t need to modify nested objects independently.
Deep Copy: Use this when we need to ensure complete independence between the original and the copied object, especially when dealing with nested structures.
Conclusion
Understanding the difference between shallow copy and deep copy in JavaScript is critical for preventing unintended changes to your data. While shallow copies are more efficient, deep copies provide complete independence between the original and copied objects, making them ideal for complex data structures.
Choosing the right method depends on your use case, and both copying techniques have their advantages and limitations. Be mindful of the behavior of references, and ensure we’re using the appropriate copy method to avoid unexpected bugs and side effects.