Understanding Shallow and Deep Copy in JavaScript
Copying Objects in JavaScript
There are various ways to copy objects in JavaScript. One is using direct assignment, another is by using spread operator. One more way is to use a for loop to iterate through each key-value pairs and add it to a new object. But, there are consequences for each particular way for how we do it. If we use the assignment operator, the new object will have the same reference as the original object, thus creating a dependency between them, i.e., either of the one is modified, another one is affected. Here, the concepts of Shallow Copy and Deep Copy comes in. We will understand what are these two, what are the pros and cons and then we will learn how to perform these operations and understand the use cases for each of them.
Shallow Copy
The term "Shallow Copy" refers to the process of creating a copy of an object, but only at the first level. This means both the original and the copied object will point to the same nested object or array. A shallow copy can be performed using the Spread "..." operator.
let userObj = {
name: "Alain",
EMPID: "U277564",
roles: {
isAdmin: false,
isRecurringUser: true,
isNightlyAllowance: true
}
};
let copyOfUserObj = {...userObj};
The above code describes how the variable copyOfUserObj
is created using the userObj
using the Spread Operator.
Now lets use an image to understand what's happening under the hood.
You can see in the image above, how the copyOfUserObj
's roles is pointing to the original object's address, and the first level properties of the new object are assigned with a new address at a different storage location.
There are several pros and cons, one of the pro is that its performant efficient and memory efficient, especially for large objects. But the con also lies there itself as the nested objects are not copied to the new address, mutable dependency between the original and the new one arises, leading to bugs.
Deep Copy
Deep Copy refers to the process of creating a new object, which is wholly stored in a different memory location, creating distinction between the two without creating mutable dependency. Lets understand this also with a diagram
As you can see, when we perform a deep copy on an object, the new instance is created and assigned to a new memory location with different address, even the number of nested objects does not matter.
There are several ways to perform Deep Copy, like using the combination of JSON.parse()
and JSON.stringify()
, by writing a recursive function, or using a library. We will look at two most easily usable ways.
Using the JSON methods
let userObj = { name: "Alain", EMPID: "U277564", roles: { isAdmin: false, isRecurringUser: true, isNightlyAllowance: true } }; let copyOfUserObj = JSON.parse(JSON.stringify(userObj));
The
copyOfUserObj
is created using the combination of JSON methods. The limitation is that this only works on JSON-safe data and we might see some bugs or intrusion if not managed properly
Using the structuredClone
let userObj = { name: "Alain", EMPID: "U277564", roles: { isAdmin: false, isRecurringUser: true, isNightlyAllowance: true } }; let copyOfUserObj = structuedClone(userObj);
This is a newer method introduced in modern browsers, this method will perform deep copy and can even handle more Data Types like Map, Set, Date, etc. The con here is that this method is not available to older browsers without a polyfill.
Conclusion
In conclusion, understanding the nuances between shallow and deep copies is crucial for JavaScript developers. While shallow copying, achievable through methods like the spread operator, is efficient for simple, top-level object duplication, it falls short with nested structures due to shared references. This can lead to unintended side-effects when modifying nested objects. On the other hand, deep copying, which can be achieved through methods like JSON.parse(JSON.stringify())
or structuredClone()
, creates a completely independent copy of the original object, including all nested elements. This independence is essential for complex data structures but comes at the cost of performance and potential limitations with certain data types. Choosing between shallow and deep copying ultimately depends on the specific requirements of your project and the data structures involved. By understanding these differences and their implications, developers can make informed decisions, leading to more robust and bug-free code.