When working with objects and arrays in JavaScript, creating copies of data structures is a common task. However, developers often face challenges when deciding between a shallow copy and a deep copy. Misunderstanding the differences can lead to unintended side effects in your code. Let’s dive into these concepts, their differences, and when to use each.
👉 Download eBook - JavaScript: from ES2015 to ES2023
What Is a Shallow Copy?
A shallow copy creates a new object with copies of the top-level properties of the original object. For properties that are primitives (e.g., numbers, strings, booleans), the value itself is copied. However, for properties that are objects (like arrays or nested objects), only the reference is copied—not the actual data.
This means that while the new object has its own copy of top-level properties, the nested objects or arrays remain shared between the original and the copy.
Example of a Shallow Copy
const original = {
name: "Alice",
details: {
age: 25,
city: "Wonderland"
}
};
// Shallow copy
const shallowCopy = { ...original };
// Modify the nested object in the shallow copy
shallowCopy.details.city = "Looking Glass";
// Original object is also affected
console.log(original.details.city); // Output: "Looking Glass"
How to Create a Shallow Copy
-
Using the Spread Operator (
...
):
const shallowCopy = { ...originalObject };
-
Using
Object.assign()
:
const shallowCopy = Object.assign({}, originalObject);
While these methods are fast and easy, they are not suitable for deeply nested objects.
What Is a Deep Copy?
A deep copy duplicates every property and sub-property of the original object. This ensures that the copy is completely independent of the original, and changes to the copy do not affect the original object.
Deep copying is essential when dealing with complex data structures like nested objects or arrays, particularly in scenarios where data integrity is critical.
Example of a Deep Copy
const original = {
name: "Alice",
details: {
age: 25,
city: "Wonderland"
}
};
// Deep copy using JSON methods
const deepCopy = JSON.parse(JSON.stringify(original));
// Modify the nested object in the deep copy
deepCopy.details.city = "Looking Glass";
// Original object remains unchanged
console.log(original.details.city); // Output: "Wonderland"
How to Create a Deep Copy
-
Using
JSON.stringify()
andJSON.parse()
:- Converts the object into a JSON string and then parses it back into a new object.
- Limitations:
- Cannot handle circular references.
- Ignores properties like functions,
undefined
, orSymbol
.
const deepCopy = JSON.parse(JSON.stringify(originalObject));
-
Using Libraries:
- Libraries like Lodash provide robust deep cloning methods.
const _ = require('lodash');
const deepCopy = _.cloneDeep(originalObject);
-
Custom Recursive Function:
- For full control, you can write a recursive function to clone nested objects.
Comparing Shallow Copy and Deep Copy
Feature | Shallow Copy | Deep Copy |
---|---|---|
Scope | Copies only top-level properties. | Copies all levels, including nested data. |
References | Nested references are shared. | Nested references are independent. |
Performance | Faster and lightweight. | Slower due to recursive operations. |
Use Cases | Flat or minimally nested objects. | Deeply nested objects or immutable structures. |
When to Use Shallow Copy
- Flat Objects: When dealing with simple objects without nested properties.
- Performance: When speed is crucial, and you don’t need to handle deeply nested data.
- Temporary Changes: When you intend to modify top-level properties but share nested data.
Example Use Case
Copying a user’s settings object to make quick adjustments:
const userSettings = { theme: "dark", layout: "grid" };
const updatedSettings = { ...userSettings, layout: "list" };
When to Use Deep Copy
- Complex Structures: For objects with multiple levels of nesting.
- Avoiding Side Effects: When you need to ensure that changes in the copy don’t affect the original.
- State Management: In frameworks like React or Redux, where immutability is critical.
Example Use Case
Duplicating the state of a game or application:
const gameState = {
level: 5,
inventory: {
weapons: ["sword", "shield"],
potions: 3
}
};
// Deep copy ensures no side effects
const savedState = JSON.parse(JSON.stringify(gameState));
Common Mistakes and Pitfalls
-
Assuming Shallow Copy Is Always Sufficient:
- Developers often mistakenly use shallow copy methods for nested objects, leading to unintended changes in the original data.
-
Overusing JSON Methods:
- While
JSON.stringify
/JSON.parse
is simple, it doesn’t work for all objects (e.g., those containing methods or circular references).
- While
-
Neglecting Performance:
- Deep copy methods can be slower, especially for large objects, so use them judiciously.
Conclusion
Understanding the difference between shallow copy and deep copy is essential for writing bug-free JavaScript code. Shallow copies are efficient for flat structures, while deep copies are indispensable for complex, nested objects. Choose the appropriate method based on your data structure and application needs, and avoid potential pitfalls by knowing the limitations of each approach.
Top comments (4)
You've completely missed
structuredClone()
as a way of deep copying - the built in function for it should be here! structuredCloneShould be the preferred method, if supported by the browser/runtime
I would like to see a native way of deep cloning - structuredClone, and more correct limitations for
JSON.parse
together withJSON.stringify
in the article.Some comments may only be visible to logged-in visitors. Sign in to view all comments.