Efficient Ways to Deep Clone an Object in JavaScript
Cloning an object in JavaScript requires creating a duplicate where changes to the clone don't affect the original. Deep cloning involves copying all properties, including nested objects. Below are the most efficient methods to achieve deep cloning, with their pros and cons.
1. Using JSON.parse()
and JSON.stringify()
This method converts an object into a JSON string and then parses it back into a new object.
Example:
const obj1 = { name: "John", age: 30, address: { city: "New York" } };
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.address.city = "Los Angeles";
console.log(obj1.address.city); // Output: "New York"
Advantages:
- Simple and concise.
- Works well for objects with primitive and nested values.
Limitations:
-
Doesn't support:
- Functions.
-
undefined
values. - Symbols.
- Circular references.
- Converts special objects (e.g.,
Date
,Set
,Map
) into plain objects.
2. Using structuredClone()
The structuredClone()
method is a native JavaScript API that deeply clones objects, supporting more data types than JSON.parse()
and JSON.stringify()
.
Example:
const obj1 = { name: "Alice", age: 25, date: new Date() };
const obj2 = structuredClone(obj1);
obj2.date.setFullYear(2025);
console.log(obj1.date.getFullYear()); // Original date remains unchanged
Advantages:
- Supports complex types like
Date
,Set
,Map
, andTypedArray
. - Handles circular references gracefully.
Limitations:
- Requires a modern browser or Node.js (v17+).
- Slightly less efficient than
JSON.parse()
for simple objects.
3. Using Lodash’s cloneDeep()
Lodash is a utility library that offers a reliable cloneDeep()
function for deep cloning.
Example:
const _ = require("lodash");
const obj1 = { name: "Mark", hobbies: ["reading", "traveling"] };
const obj2 = _.cloneDeep(obj1);
obj2.hobbies.push("coding");
console.log(obj1.hobbies); // Output: ["reading", "traveling"]
Advantages:
- Handles all data types, including circular references.
- Reliable for production-grade applications.
Limitations:
- Requires installing Lodash (
npm install lodash
). - Adds a dependency to your project.
4. Using Recursive Functions
For small, custom use cases, you can write your own deep cloning function.
Example:
const deepClone = (obj) => {
if (obj === null || typeof obj !== "object") return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
const clonedObj = {};
for (const key in obj) {
clonedObj[key] = deepClone(obj[key]);
}
return clonedObj;
};
const obj1 = { name: "Emma", address: { city: "London", zip: 12345 } };
const obj2 = deepClone(obj1);
obj2.address.city = "Paris";
console.log(obj1.address.city); // Output: "London"
Advantages:
- Full control over the cloning process.
- No external library required.
Limitations:
- More verbose and error-prone.
- Needs enhancements to handle special objects like
Date
,Set
,Map
.
5. Using Spread Syntax or Object.assign()
(Shallow Cloning)
These methods create a shallow copy, duplicating only the top-level properties. For deep cloning, this approach doesn't suffice.
Example:
const obj1 = { name: "Liam", address: { city: "Berlin" } };
const obj2 = { ...obj1 };
obj2.address.city = "Munich";
console.log(obj1.address.city); // Output: "Munich" (Not a deep clone)
Comparison of Methods
Method | Supports Circular Refs | Supports Special Types | Performance | Use Case |
---|---|---|---|---|
JSON.parse/JSON.stringify |
No | No | High | Simple objects, non-circular refs. |
structuredClone() |
Yes | Yes | Moderate | Complex objects, modern environments. |
Lodash.cloneDeep() |
Yes | Yes | Moderate | Production applications. |
Custom Recursive | Optional | No | Varies | Custom lightweight solutions. |
Spread / Object.assign
|
No | No | High | Shallow cloning only. |
Conclusion
-
Use
structuredClone()
if you need a built-in solution for modern environments. -
Use
JSON.parse()
andJSON.stringify()
for simple and performance-critical scenarios. -
Use Lodash's
cloneDeep()
for reliable, production-grade deep cloning. - Write a custom function for unique, lightweight use cases.
Top comments (0)