In JavaScript, objects are a fundamental data structure used to store collections of key-value pairs. When working with objects, it's common to need to make copies of them. There are two types of copying: shallow copying and deep copying. Shallow copying only creates a new reference to the original object, so any changes made to the copy will affect the original object. Deep copying, on the other hand, creates a new object with completely new references to the original object's properties, so changes made to the copy will not affect the original object.
There are several methods to deep copy objects in JavaScript, each with its own advantages and use cases. The most commonly used method is to use a combination of Object.assign()
or the spread operator (...
) to create a shallow copy of the object, and then recursively copy each nested property of the object to create a complete deep copy. Another method is to use the JSON.parse()
and JSON.stringify()
methods to convert the object to and from JSON format, effectively creating a deep copy of the object.
In addition to these methods, there are also third-party libraries such as Lodash and jQuery that provide their own deep copying methods. It's important to choose the appropriate method based on the specific use case to optimize code for performance and maintainability
Different methods to deep copy in JavaScript
Here are some different methods for deep copying objects in JavaScript:
- Object.assign() method - creates a new object with properties from existing objects
- Spread operator (
...
) - spreads the properties of an object into a new object JSON.parse()
andJSON.stringify()
methods - convert objects to and from JSON formatlodash.cloneDeep()
method - creates a deep copy of an object using the Lodash libraryjQuery.extend()
method - creates a deep copy of an object using the jQuery library- Using the recursion method - recursively creates a deep copy of an object by iterating through its properties and copying each nested property individually
1. Using the spread operator
The spread operator, denoted by three dots (...
), can be used to create a new array or object that contains all the properties and values of the original. This creates a completely new object or array, with its own memory allocation, ensuring that any modifications made to the new object or array will not affect the original.
Let’s illustrate how we can deep copy an object say for example, an array in JavaScript using the spread operator
const originalArray = [13, 874, 192];
const newArray = [...originalArray];
console.log(newArray);
newArray[1] = 35;
console.log(originalArray)
console.log(newArray);
Output
[ 13, 874, 192 ]
[ 13, 874, 192 ]
[ 13, 35, 192 ]
In the above, we are able to copy the original array and create a new array without affecting the original array when we assign a new element to the index position 1.
We can apply the same approach to a full object as you can see below
const originalObject = {
name: "Femi",
skill: "frontend",
language: "javascript",
level: 2,
};
const newObject = { ...originalObject };
console.log(newObject);
Output
{ name: 'Femi', skill: 'frontend', language: 'javascript', level: 2 }
2. Using the JSON.parse()
and JSON.stringify()
method
This approach involves converting the original object or array to a JSON string, and then parsing the string back into a new object or array. This creates a completely new object or array, with its own memory allocation, ensuring that any modifications made to the new object or array will not affect the original.
Here's an example of how we can achieve a deep copy with this approach
const originalObject = {
name: "Deepak",
age: 45,
hobbies: ["blogging", "reading"],
};
const newObject = JSON.parse(JSON.stringify(originalObject));
newObject.name = "Dave";
newObject.hobbies.push("drawing");
console.log(originalObject);
console.log(newObject);
Output
{ name: 'Deepak', age: 45, hobbies: [ 'blogging', 'reading' ] }
{
name: 'Dave',
age: 45,
hobbies: [ 'blogging', 'reading', 'drawing' ]
}
In this example, we use JSON.stringify()
to convert our original object into a string, and then use JSON.parse() to create a new object based on that string. By doing this, we create a new, independent copy of the original object that we can modify without affecting the original.
While this method is simple and effective, it does have some limitations. One potential issue is that it doesn't handle circular references. In other words, if your object contains a reference to itself or to another object that refers back to it, the JSON.stringify()
method will throw an error.
Another downside is that it can be inefficient for large objects or arrays, as it involves converting the data to a string and then parsing it back into an object or array.
So what's the alternative? Well, there are other ways to deep copy in JavaScript that handle these limitations more effectively. One such method is to use Object.assign()
3. Using the Object.assign()
method to deep copy
The Object.assign()
method can be used to copy the values of all enumerable properties from one or more source objects to a target object. This creates a completely new object, with its own memory allocation, ensuring that any modifications made to the new object will not affect the original.
To illustrate, we will use the same example in the last section
const originalObject = {
name: "Deepak",
age: 45,
hobbies: ["blogging", "reading"],
};
const newObject = Object.assign({}, originalObject);
newObject.name = "Dave";
console.log(originalObject);
console.log(newObject);
Output
{ name: 'Deepak', age: 45, hobbies: [ 'blogging', 'reading' ] }
{ name: 'Dave', age: 45, hobbies: [ 'blogging', 'reading' ] }
4. Using lodash.cloneDeep()
method
Lodash is a JavaScript utility library that provides a wide range of functions for working with arrays, objects, strings, and more. The lodash.cloneDeep()
method is one of the utility functions provided by Lodash and is used to create a deep copy of an object.
Here's an example of using the lodash.cloneDeep()
method to create a deep copy of an object:
const originalObject = {
name: "Deepak",
age: 45,
hobbies: ["blogging", "reading"],
};
const copyObject = _.cloneDeep(originalObject);
copyObject.hobbies.push("swimming");
console.log(originalObject.hobbies); // Output: ["blogging", "reading"]
console.log(copyObject.hobbies); // Output: ["blogging", "reading", "swimming"]
In this example, we define an originalObject
variable that contains an object with nested properties. We then use the _.cloneDeep()
method from the Lodash library to create a deep copy of the originalObject
and store it in a variable copyObject
. We then modify the hobbies
property of the copyObject
to include a new value. Finally, we log the hobbies
property of both the originalObject
and the copyObject
to demonstrate that changes made to the copy do not affect the original object.
The output shows that the value of the hobbies
property of the originalObject
is still ["blogging", "reading"]
and has not been affected by the modification made to the copyObject
. This is because the _.cloneDeep()
method created a completely new object with completely new references to the original object's properties, effectively creating a deep copy of the object.
5. Using jQuery.extend()
method
jQuery is a popular JavaScript library that provides a wide range of functions for working with HTML documents, events, and more. The jQuery.extend() method is one of the functions provided by jQuery and can be used to merge the contents of two or more objects together into a target object. This method can also be used to create a deep copy of an object by providing an empty object as the target object and the object to be copied as the source object.
Here's an example of using the jQuery.extend()
method to create a deep copy of an object:
const originalObject = {
name: "Deepak",
age: 45,
hobbies: ["blogging", "reading"],
};
// Using jQuery.extend() method
const copyObject = $.extend(true, {}, originalObject);
copyObject.hobbies.push("swimming");
console.log(originalObject.hobbies); // Output: ["blogging", "reading"]
console.log(copyObject.hobbies); // Output: ["blogging", "reading", "swimming"]
In this example, we use the $.extend()
method to create a deep copy of the originalObject
variable and store it in the copyObject
variable. We then modify the hobbies
property of the copyObject
variable to include a new value, swimming
. Finally, we log the hobbies
property of both the originalObject
and the copyObject
variables to demonstrate that changes made to the copy do not affect the original object.
6. Using the recursion method to create a deep copy of an object
The recursion method involves iterating through an object and copying each property individually. If a property is an object, we call the function recursively to copy the nested object.
const originalObject = {
name: "Deepak",
age: 45,
hobbies: ["blogging", "reading"],
};
// Using recursion
function deepCopy(obj) {
let copy = {};
for (let key in obj) {
if (typeof obj[key] === "object") {
copy[key] = deepCopy(obj[key]);
} else {
copy[key] = obj[key];
}
}
return copy;
}
const copyObject = deepCopy(originalObject);
copyObject.hobbies.push("coding");
console.log(originalObject.hobbies); // Output: ["blogging", "reading"]
console.log(copyObject.hobbies); // Output: ["blogging", "reading", "coding"]
In this example, we define a deepCopy()
function that takes an object as an argument and returns a deep copy of the object. We then use the deepCopy()
function to create a deep copy of the originalObject
variable and store it in the copyObject
variable. We then modify the hobbies
property of the copyObject
variable to include a new value, coding
. Finally, we log the hobbies
property of both the originalObject
and the copyObject
variables to demonstrate that changes made to the copy do not affect the original object.
Summary
In summary, creating a deep copy of an object in JavaScript is essential when you need to create a copy that is independent of the original object. There are several methods for creating a deep copy of an object in JavaScript, each with its own advantages and disadvantages.
One of the most common methods is to use the Object.assign()
method or the spread operator (...
) to create a shallow copy of an object, and then use recursion or a library like Lodash or jQuery to create a deep copy of the nested properties. Another method is to use JSON.parse()
and JSON.stringify()
to create a deep copy of an object, but this method is less efficient and may not work with all types of objects.
It's important to note that a shallow copy of an object only creates a new reference to the same nested objects, which means that any modifications made to the nested objects will affect both the original and copied objects. On the other hand, a deep copy creates a completely new object with completely new references to the nested objects, which means that modifications made to the copied object will not affect the original object.
References
Object.assign() - JavaScript | MDN (mozilla.org)
JSON.parse() - JavaScript | MDN (mozilla.org)
JSON.stringify() - JavaScript | MDN (mozilla.org)