Here are more examples and details for each method to deep clone objects in JavaScript:
1. structuredClone()
(Modern Approach)
Supports: Objects, Arrays, Dates, RegExp, Maps, Sets, ArrayBuffers, and more.
Example 1: Basic Object
const original = {
name: "Alice",
hobbies: ["reading", "hiking"],
createdAt: new Date()
};
const clone = structuredClone(original);
console.log(clone.createdAt instanceof Date); // true (not a string!)
Example 2: Circular References
const original = { a: 1 };
original.self = original; // Circular reference
const clone = structuredClone(original);
console.log(clone.self === clone); // true (preserves circularity)
Example 3: Typed Arrays
const buffer = new ArrayBuffer(16);
const original = { buffer };
const clone = structuredClone(original);
console.log(clone.buffer.byteLength); // 16 (copied buffer)
2. JSON.parse(JSON.stringify())
(Legacy Fallback)
Use for: Simple, JSON-safe data.
Example 1: Loses Data Types
const original = {
date: new Date(),
fn: () => console.log("test")
};
const clone = JSON.parse(JSON.stringify(original));
console.log(clone.date); // string (e.g., "2024-05-20T12:00:00.000Z")
console.log(clone.fn); // undefined (functions removed)
Example 2: Fails on Circular References
const obj = { a: 1 };
obj.self = obj;
JSON.parse(JSON.stringify(obj)); // Throws "TypeError: Converting circular structure to JSON"
3. Lodash _.cloneDeep()
(Third-Party)
Use for: Legacy projects or complex data with broad browser support.
Example 1: Cloning Complex Types
import _ from 'lodash';
const original = {
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3])
};
const clone = _.cloneDeep(original);
console.log(clone.map.get('key')); // "value" (Map preserved)
Example 2: Prototype Inheritance
class Person {
constructor(name) { this.name = name; }
}
const original = new Person("Alice");
const clone = _.cloneDeep(original);
console.log(clone instanceof Person); // false (prototype chain not copied)
4. Custom Recursive Cloner
Use for: Edge cases (e.g., cloning functions or custom classes).
Example: Basic Implementation
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj); // Handle circular references
const clone = Array.isArray(obj) ? [] : {};
hash.set(obj, clone);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
return clone;
}
const original = { a: 1, nested: { b: 2 } };
const clone = deepClone(original);
console.log(clone.nested === original.nested); // false (deep clone)
5. Performance Considerations
structuredClone()
is ~2x faster thanJSON.parse(JSON.stringify())
for large objects.- Lodash is slower than
structuredClone()
but handles more edge cases. - Avoid cloning massive objects (>1MB) frequently—it can block the main thread.
Summary Table
Method | Speed | Handles Circular Refs | Preserves Types | Functions | Dependencies |
---|---|---|---|---|---|
structuredClone() | Fast | ✅ | ✅ | ❌ | None |
JSON.parse/stringify | Medium | ❌ | ❌ | ❌ | None |
Lodash _.cloneDeep() | Slow | ✅ | ❌ | ❌ | Lodash |
Custom Recursive Cloner | Varies | ✅ (if implemented) | ✅ (custom) | ✅ (custom) | None |
When to Use What?
- Modern Apps: Always prefer
structuredClone()
. - Legacy Browsers: Use Lodash or
JSON.parse/stringify
. - Specialized Needs: Write a custom cloner (e.g., cloning functions or classes).