Deep Copy vs. Shallow Copy in JavaScript: A Deep Dive
Rohit Bhat ★
Engineering fellow @LearningMate | Software Architect | JavaScript | Angular | React | RxJS | NgRX
Understanding Object Copying, References, and Memory Behavior
Introduction
Copying objects in JavaScript is a common task, but understanding how objects are stored in memory and how copying works is crucial. Without this knowledge, we may accidentally modify the original object when making a copy, leading to unexpected behavior and hard-to-find bugs.
This article will cover:
? What shallow copy and deep copy mean.
? How JavaScript handles memory and references.
? Different methods to create shallow and deep copies.
? In-depth explanation of structuredClone() and lodash.cloneDeep().
? Understanding circular references and how to handle them.
? Use cases and limitations of each method.
? When to use which technique.
1. How JavaScript Stores Data in Memory
Before we understand copying, let's explore how JavaScript stores different types of data in memory.
Primitive vs. Reference Types
In JavaScript, data is stored in two ways:
Primitive Data Types (Stored by Value)
Primitive types include:
? number
? string
? boolean
? null
? undefined
? symbol
? bigint
Example:
let a = 10;
let b = a; // A copy of 'a' is assigned to 'b'
b = 20;
console.log(a); // 10 (remains unchanged)
console.log(b); // 20
?? Explanation: The value of a is copied into b. Changing b does not affect a because primitive values are stored separately in memory.
Reference Data Types (Stored by Reference)
Reference types include:
? Objects {}
? Arrays []
? Functions
? Maps, Sets, Dates, RegEx
Example:
let obj1 = { name: "Alice" };
let obj2 = obj1; // obj2 holds a reference to obj1
obj2.name = "Bob";
console.log(obj1.name); // "Bob" (both variables point to the same object)
?? Explanation: obj2 does not store a new object. Instead, it stores a reference (pointer) to the same object in memory. Changing obj2.name affects obj1.name as well.
2. What is a Shallow Copy?
A shallow copy creates a new object but still shares references to nested objects.
Shallow Copy Example
let original = { name: "John", details: { age: 30 } };
let shallowCopy = { ...original }; // Spread operator creates a shallow copy
shallowCopy.name = "Doe";
shallowCopy.details.age = 40; // Modifying nested object
console.log(original.name); // "John" (not affected)
console.log(original.details.age); // 40 (affected)
?? Explanation:
Methods for Shallow Copy
Limitations of Shallow Copy
? Does not fully clone nested objects or arrays.
? Still maintains references to deeper levels of data.
? Not suitable for deeply nested structures.
3. What is a Deep Copy?
A deep copy creates a new object and recursively duplicates all levels, ensuring no references remain to the original object.
Deep Copy Example
let original = { name: "John", details: { age: 30 } };
let deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.details.age = 40;
console.log(original.details.age); // 30 (unchanged)
Methods for Deep Copy
4. Understanding Circular References
A circular reference occurs when an object references itself directly or indirectly, creating an infinite loop in memory.
Example of Circular Reference
let obj = { name: "Alice" };
obj.self = obj; // Circular reference
console.log(obj.self.self.self.name); // "Alice" (keeps referencing itself)
?? Problem: If we try to use JSON.stringify() on this object, it will throw an error:
JSON.stringify(obj); // TypeError: Converting circular structure to JSON
?? Solution:
5. Understanding structuredClone()
structuredClone() is a built-in method in modern browsers that performs a true deep copy, including handling circular references.
Example
let obj = { name: "Alice" };
obj.self = obj;
let deepCopy = structuredClone(obj);
console.log(deepCopy.self.name); // "Alice"
Advantages of structuredClone()
? Handles nested structures properly.
? Supports complex types (Date, Map, Set, ArrayBuffer, etc.).
? Preserves undefined values.
? Supports circular references.
Limitations of structuredClone()
? Does not work with functions.
? Not available in older browsers.
6. Understanding Lodash’s cloneDeep()
Lodash’s cloneDeep() is a powerful deep-copying utility that supports functions and circular references.
Example
let obj = { name: "Alice" };
obj.self = obj;
let deepCopy = _.cloneDeep(obj);
console.log(deepCopy.self.name); // "Alice"
Advantages of lodash.cloneDeep()
? Handles complex nested objects.
? Supports functions, circular references, and special objects.
? Works across all browsers.
Limitations of lodash.cloneDeep()
? Slightly slower than structuredClone().
? Requires external dependency (lodash).
7. Comparing All Methods: Which One to Use?
8. Conclusion
? Use a shallow copy when modifying only top-level properties.
? Use JSON methods for simple objects (but avoid circular structures).
? Use structuredClone() for modern apps with complex data.
? Use lodash.cloneDeep() when handling circular references and functions.
Now you have a solid understanding of deep copy vs. shallow copy in JavaScript! ??