Shallow Copy vs Deep Copy in JavaScript: Which One Should You Use?

AUTHOR

Tien Nguyen

the process of creating a new object or array with the same values as an existing one.
Copying is an essential technique in programming that helps you to avoid unexpected mutations to objects and arrays There are two main techniques for copying in JavaScript: shallow copy and deep copy.
In this article, we’ll explore the di몭erences between shallow copy vs deep copy in JavaScript and when to use each technique We’ll also discuss the bene몭ts and limitations of each approach and provide you with best practices to help you avoid common pitfalls.
By the end of this post, you’ll have a better understanding of how to use shallow copy and deep copy in your code e몭ectively. So, let’s dive in!
Table of Contents
1 Shallow Copy in JavaScript
1.1. Spread Operator
1 2 Object assign()
1.3. Array.slice()
2 Deep Copy in JavaScript
2.1. JSON.parse(JSON.stringify())
2 2 lodash cloneDeep()
2.3. Recursive Function
3 Comparing Shallow Copy vs Deep Copy in JavaScript
3.1. Level of Copying
3 2 Reference Handling
3.3. Performance
3 4 Circular References
4 Pros and Cons of Shallow Copy
4.1. Pros
4 2 Cons
5. Pros and Cons of Deep Copy
5 1 Pros
5.2. Cons
6 Use Cases for Shallow Copy and Deep Copy
6.1. When to Use Shallow Copy
6 2 When to Use Deep Copy
7. Common Pitfalls and Best Practices
71 Modifying Original Objects or Arrays
7.2. Performance Considerations
7 3 Choosing the Right Copying Method
7.4. Using Libraries and Frameworks
8. Conclusion
Shallow Copy in JavaScript
JavaScript provides multiple ways to create shallow copies of objects and arrays.
When you make a shallow copy of an object or array, you create a new one that points to the same values as the original If you change the original object or array, the shallow copy will also change. Reversely, if you change the shallow copy, the original object or array will also be a몭ected.
A shallow copy does not clone nested objects, meaning that if the original object contains other objects or arrays as properties, the shallow copy will point to the same references as the original.
Spread Operator
9 FAQs …
One common way to create a shallow copy of an object or an array in JavaScript is to use the spread operator ( ) The spread operator copies all enumerable properties of an object or an array into a new one. For example:
1. // Create an original object
2. let person = {
3 name: 'Alice',
4 age: 25,
5 hobbies: ['reading', 'writing', 'coding'], 6 }
7.
//
9. let personCopy = { ...person }
8. Create a shallow copy using the spread operator11 // Modify the original object
12. person.name = 'Bob'
13.
14. // Check the shallow copy
15. console.log(personCopy.name) // Alice
16 console.log(personCopy.hobbies) // ["reading", "writing", "coding"]
17
18 // Modify the shallow copy
19 personCopy.hobbies.push('gaming')
20.
21. // Check the original object
22. console.log(person.hobbies) // ["reading", "writing", "coding", "gaming"]
As you can see, the spread operator makes a copy of an object and its properties; however, it doesn’t create a copy of nested arrays within the object. Therefore, when we modify the property of either object, the change a몭ects both objects
hobbies
Object.assign()
Another way to create a shallow copy of an object is to use the method. It copies all the enumerable and own properties of one or more source objects into a target object. For instance:
1 // Create an original object
2. let person = {
3. name: 'Alice',
4. age: 25,
5. hobbies: ['reading', 'writing', 'coding'], 6 } 7
8 // Create a shallow copy using Object.assign()
9 let personCopy = Object.assign({}, person)
10.
11. // Modify the original object
12. person.name = 'Bob'
13.
14. // Check the shallow copy
15 console.log(personCopy.name) // Alice
Object.assign()
16 console log(personCopy hobbies) // ["reading", "writing", "coding"]
17
18 // Modify the shallow copy 19. personCopy.hobbies.push('gaming')
20.
21. // Check the original object 22. console.log(person.hobbies) // ["reading", "writing", "coding", "gaming"]
Object.assign()
The method works similarly to the spread operator, except that it can take multiple source objects as arguments and merge them into the target object. However, it also does not clone nested objects, so modifying them will a몭ect both objects
Array.slice()
To create a shallow copy of an array, you can use the method. This method returns a new array with a portion of the original array. If no arguments are provided, it returns a copy of the whole array.
Array.slice()
The method creates a new array with the same elements as the original array, but it does not clone nested arrays or objects. Therefore, modifying them will a몭ect both arrays. Here’s an example:
Array.slice()
1 // Create an original array with a nested array and an object as elements
=
Create a new array using Array.slice()
Check the values and references of the original array and the
{name: "Alice", age:
{name: "Alice", age: 25}]
(originalArray[0] === newArray[0]); // true
// Modify the nested array and the object in the original array
originalArray[0] push(4);
originalArray[1].name = "Bob";
// Check the values and references of the original array and the new array
console.log(originalArray); // [[1, 2, 3, 4], {name: "Bob", age: 25}]
console log(newArray); // [[1, 2, 3, 4], {name: "Bob", age: 25}]
Deep Copy in JavaScript
Creating a deep copy of an object or an array in JavaScript involves duplicating the
original object’s properties and values, including any nested objects or arrays, into a new object or array without any shared references. This means that changes made to one object or array will not a몭ect the other.
There are various ways to create a deep copy:
JSON.parse(JSON.stringify())
JSON.parse(JSON.stringify())
One simple method is using the method, which converts an object or array into a JSON string and then parses it back into a new object or array For instance:
1 // Create an original object
2 let person = {
3 name: 'Alice',
4 age: 25,
5. hobbies: ['reading', 'writing', 'coding'],
6. }
7.
8 // Create a deep copy using JSON parse(JSON stringify())
9 let personCopy = JSON parse(JSON stringify(person)) 10
11 // Modify the original object 12 person.name = 'Bob' 13
14. // Check the deep copy
15. console.log(personCopy.name) // Alice
16. console.log(personCopy.hobbies) // ["reading", "writing", "coding"] 17 18 // Modify the deep copy
personCopy.hobbies.push('gaming')
21 // Check the original object
22. console.log(person.hobbies) // ["reading", "writing", "coding"]
As you can see, the method creates a new object with the same values and properties as the original object, but it also clones the nested array Therefore, when we modify the hobbies property of either object, the change does not a몭ect the other object.
lodash.cloneDeep()
JSON.parse(JSON.stringify()) lodash
Another way is to use a third-party library such as , which provides the method that recursively clones an object or an array and any nested objects or arrays For example:
cloneDeep()
1. // Import lodash.cloneDeep
2. import cloneDeep from 'lodash/cloneDeep'
3
4 // Create an original object
5 let person = {
6 name: 'Alice',
7. age: 25,
8. hobbies: ['reading', 'writing', 'coding'],
}
11 // Create a deep copy using lodash cloneDeep() 12 let personCopy = cloneDeep(person)
14 // Modify the original object
15 person.name = 'Bob'
16.
17. // Check the deep copy
18. console.log(personCopy.name) // Alice
19. console.log(personCopy.hobbies) // ["reading", "writing", "coding"]
20
21 // Modify the deep copy
22 personCopy.hobbies.push('gaming')
23
24. // Check the original object
25. console.log(person.hobbies) // ["reading", "writing", "coding"]
lodash.cloneDeep()
JSON.parse(JSON.stringify())
The function works similarly to the method, except that it does not rely on JSON serialization and deserialization It also does not clone functions or symbols, so it may not work for some complex objects.
JSON.parse(JSON.stringify())
lodash.cloneDeep()
However, unlike , can handle circular references and unde몭ned values, and it can clone other data types or methods that JSON cannot serialize, such as dates, regexes, maps, sets, etc., by using customizer functions or plugins.
Recursive Function
Alternatively, you can write a custom recursive function that iterates over the properties of an object or an array and creates new copies of them For instance:
1 // Create a custom function for deep copying
2 function deepCopy(obj) {
3 // Check if obj is an array or an object
4 if (Array.isArray(obj)) {
5. // Create a new array and loop over its elements
6. let newArr = []
7. for (let i = 0; i < obj.length; i++) {
8. // Recursively call deepCopy on each element and push it to newArr 9 newArr push(deepCopy(obj[i]))
} 11 // Return newArr
12. return newArr
13. } else if (typeof obj === 'object' && obj !== null) {
14. // Create a new object and loop over its keys 15 let newObj = {}
16 for (let key in obj) { 17 // Recursively call deepCopy on each value and assign it to newObj[key]
}
newObj[key] = deepCopy(obj[key])
// Return newObj
return newObj
} else {
// Return obj as it is (primitive value)
return obj
}
}
// Create an original object
let person = {
name: 'Alice',
age: 25,
hobbies: ['reading', 'writing', 'coding'],
}
// Create a deep copy using deepCopy()
let personCopy = deepCopy(person)
// Modify the original object
person.name = 'Bob'
// Check the deep copy
console.log(personCopy.name) // Alice
console.log(personCopy.hobbies) // ["reading", "writing", "coding"]
// Modify the deep copy
personCopy hobbies push('gaming')
// Check the original object
console.log(person.hobbies) // ["reading", "writing", "coding"] deepCopy() hobbies
You can see that the function returns a new object with the same values and properties as the original object, but it also clones the nested array Therefore, when we modify the property of either object, the change does not a몭ect
the other object.
Comparing Shallow Copy vs Deep Copy in JavaScript
Now that we have a good understanding of what shallow copy and deep copy are, let’s take a closer look at the di몭erences between them.
Key
Di몭erenc e Shallow Copy Deep Copy
Level of Copying
Reference Handling
Performan ce
Copies only top-level properties or elements
Copies references to nested objects or arrays
Less expensive in terms of performance and memory usage
Creates a complete and independent copy of the entire object or array, including all nested objects or arrays
Creates new instances of all nested objects or arrays
More expensive in terms of performance and memory usage
Key
Di몭erenc e Shallow Copy
Circular Reference s
Handles circular references better
Level of Copying
Deep Copy
Creates circular references in the copy
The primary di몭erence between shallow copy and deep copy is the level of copying they perform. Shallow copy only copies the top-level properties or elements of an object or array, while deep copy creates a complete and independent copy of the entire object or array, including all nested objects or arrays.
Reference Handling
Another signi몭cant di몭erence between shallow copy and deep copy is how they handle references. Shallow copy only copies references to nested objects or arrays, while deep copy creates new instances of all nested objects or arrays.
Performance
Deep copy can be more expensive in terms of performance and memory usage than shallow copy This is because deep copy requires more processing power and memory to create and store a complete copy of the original object or array.
Circular References
Shallow copy can handle circular references better than deep copy. When copying an object or array with a circular reference, shallow copy will copy the reference to the original object or array, while deep copy will also create a circular reference in the copy
Pros and Cons of Shallow Copy
Here are some of the pros and cons of using shallow copy in JavaScript:
Pros
Saves Memory: Shallow copy can be useful in cases where you want to save memory by avoiding the creation of a completely new object or array By copying
only the top-level properties or elements of the original object or array, you can save memory without a몭ecting the original object or array.
Quick and Simple: Shallow copy is generally faster and simpler than deep copy, since it only copies the top-level properties or elements of an object or array This can be useful in cases where speed and simplicity are important.
Preserves References: Shallow copy preserves references to the original object or array, which can be useful in certain scenarios where you want to maintain a connection between the original and copied objects or arrays
Cons
Incomplete Copy: Shallow copy only copies the top-level properties or elements of an object or array. This means that any nested objects or arrays are not copied, but instead, their references are copied. This can lead to unintended side e몭ects or changes to the original object or array
Potential for Overwriting: Shallow copy can also lead to the potential for overwriting the original object or array, especially when working with mutable objects or arrays. Any changes made to the shallow copy can also a몭ect the original object or array
Limited Use Cases: Shallow copy is best suited for simple objects or arrays with only top-level properties or elements. For more complex data structures, such as nested objects and arrays or objects with circular references, shallow copy may not be su몭cient
Pros and Cons of Deep Copy
Here are the pros and cons of using deep copy in JavaScript:
Pros
Some of the pros of using deep copy in JavaScript are:
Creates Independent Copies: Deep copy creates completely independent copies of an object or array, which can be useful in cases where you want to avoid unintended side e몭ects or changes to the original object or array.
More Accurate: Deep copy is generally more accurate than shallow copy, since it creates a true copy of all nested objects and arrays. This can be important in cases where accuracy is critical.
Can Handle Complex Objects and Arrays: Deep copy is designed to handle complex objects and arrays, even those with circular references or nested objects and arrays. This makes it useful in scenarios where you’re working with large or complex data structures.
Cons
Some of the cons of using deep copy in JavaScript are:
Slower Performance: Deep copy can be slower and more resource-intensive than shallow copy, especially for large or complex data structures. This can impact the overall performance of your code.
Increased Memory Usage: Deep copy can also lead to increased memory usage, since it creates completely independent copies of all nested objects and arrays This can be a concern for memory-intensive applications.
Di몭culty Handling Circular References: While deep copy is designed to handle circular references, it can still be a challenge to implement in certain scenarios. This can lead to errors or unexpected behavior in your code
Use Cases for Shallow Copy and Deep Copy
Shallow copy and deep copy have di몭erent use cases depending on the speci몭c needs of your code Here are some common scenarios where each method may be more appropriate:
When to Use Shallow Copy
When you need to create a new object or array that shares some of the properties or elements of an existing object or array, but you want to avoid a몭ecting the original object or array
When you need to pass an object or array as a parameter to a function and want to avoid unintended side e몭ects by creating a copy of the object or array.
When you need to quickly create a shallow copy of a small and simple object or array.
When to Use Deep Copy
When you need to create a completely independent copy of an object or array, without any shared references to the original object or array
When you need to make changes to a copied object or array without a몭ecting the original object or array.
When you need to create a deep copy of a large or complex object or array, even if it takes longer than shallow copy
Common Pitfalls and Best Practices
While working with shallow copy and deep copy in JavaScript, there are a few common pitfalls to be aware of, as well as some best practices to follow
Modifying Original Objects or Arrays
One common pitfall when working with shallow copy and deep copy is modifying the original object or array after creating a copy. If you modify the original object or array, the changes will also be re몭ected in the copy, which can lead to unexpected behavior.
To avoid this, you should always make a copy of an object or array before modifying it, and then work with the copy instead of the original.
Performance Considerations
As mentioned earlier, deep copy can be more expensive in terms of performance and memory usage than shallow copy. If you need to create a deep copy of a large or complex object or array, it can take a signi몭cant amount of time and memory
To optimize performance, you should only use deep copy when necessary and try to 몭nd alternative solutions when possible.
Choosing the Right Copying Method
Choosing the right copying method is crucial when working with objects or arrays in JavaScript. If you only need to create a copy of the top-level properties or elements, then shallow copy is the way to go. If you need to create a complete and independent copy of an object or array, then deep copy is the better choice.
Using Libraries and Frameworks
There are many libraries and frameworks available that provide built-in methods for copying objects and arrays in JavaScript. These libraries and frameworks can save you time and e몭ort when working with complex objects or arrays.
Some popular libraries and frameworks for copying objects and arrays in JavaScript include Lodash and Ramda.
Conclusion
In JavaScript, shallow copy and deep copy are two commonly used techniques for copying objects and arrays. While both methods can be useful in di몭erent scenarios, they have their own advantages and disadvantages.
Shallow copy is a faster and more memory-e몭cient method for copying objects and arrays It only creates a new reference to the original object or array, and any changes made to the shallow copy will also a몭ect the original. This makes it a good option for simple data structures with only top-level properties or elements.
On the other hand, deep copy creates a completely independent copy of the original object or array, including any nested objects and arrays. While this method is slower and more memory-intensive than shallow copy, it provides a true copy of the original and can be a better option for more complex data structures
When deciding which method to use, it’s important to consider the speci몭c needs of your application and data structures. In some cases, a combination of shallow copy and deep copy may be the best solution
Overall, understanding the di몭erences between shallow copy and deep copy is an important part of writing e몭cient and e몭ective JavaScript code. By taking the time to carefully consider your options, you can ensure that your code is both performant and easy to maintain
Some related posts about JavaScript:
Best Books to Learn JavaScript (2023)
What is the Use of This Keyword in Javascript?
Lexical Environment in JavaScript
Advantages of Closures in JavaScript
Is TypeScript Faster Than JavaScript?