How do I correctly clone a JavaScript object to create an independent copy?
I have an object x and want to create a copy called y where modifications to y don’t affect the original x. I understand that copying objects derived from built-in JavaScript objects can introduce unwanted properties, but this isn’t a concern for my use case since I’m working with custom literal-constructed objects. What’s the proper method to clone a JavaScript object in this scenario?
Creating an independent copy of a JavaScript object requires using proper deep copy techniques to ensure that modifications to the cloned object don’t affect the original. For custom literal-constructed objects, the modern approach is to use the structuredClone() method, which creates a true deep copy while handling nested objects, circular references, and special JavaScript types properly.
Contents
- Understanding Shallow vs Deep Copy
- Modern Solution: structuredClone()
- Alternative Deep Copy Methods
- Shallow Copy Methods
- Practical Examples and Best Practices
- Performance Considerations
Understanding Shallow vs Deep Copy
The fundamental difference between shallow and deep copy lies in how they handle nested objects and references.
A shallow copy creates a new object and copies all enumerable own properties from the original object to the new object. However, for nested objects or arrays, it only copies the references, not the actual values. This means that when you modify a nested property in the copied object, it affects the original object as well source.
A deep copy, on the other hand, recursively clones all nested objects and arrays, creating completely independent copies at every level. This ensures that modifications to any part of the cloned object won’t affect the original source.
Key Insight: When working with custom literal-constructed objects that contain nested properties, you always need a deep copy to achieve true independence between the original and cloned objects.
Modern Solution: structuredClone()
The structuredClone() function, introduced in 2022, is the modern and recommended way to create deep copies of JavaScript objects. This method provides a true deep copy while automatically handling complex scenarios like circular references and special JavaScript types.
const original = {
name: "John",
age: 30,
address: {
city: "New York",
zip: "10001"
},
hobbies: ["reading", "coding"]
};
// Create a deep copy using structuredClone()
const deepCopy = structuredClone(original);
// Modify the copy
deepCopy.address.city = "Boston";
deepCopy.hobbies.push("swimming");
// Original remains unchanged
console.log(original.address.city); // "New York"
console.log(original.hobbies); // ["reading", "coding"]
Key advantages of structuredClone():
- Automatic handling of circular references: Unlike JSON methods, it can handle objects that reference themselves
- Preservation of special types: Maintains Date, Map, Set, RegExp, and other built-in types
- Transferable objects: Supports cloning objects that can be transferred between different contexts like Web Workers source
- Native implementation: No external libraries required
// Example with circular reference
const circularObj = {};
circularObj.self = circularObj;
// This works with structuredClone()
const clone = structuredClone(circularObj);
console.log(clone.self === clone); // true (preserves circular reference)
// This would fail with JSON methods
try {
JSON.parse(JSON.stringify(circularObj)); // Throws TypeError
} catch (error) {
console.log("JSON method error:", error.message);
}
Alternative Deep Copy Methods
While structuredClone() is recommended, there are other deep copy methods you might encounter or need to use in certain scenarios.
JSON.stringify() + JSON.parse()
This is a common workaround for deep copying, but it has significant limitations:
const original = { name: "John", details: { age: 30 } };
const deepCopy = JSON.parse(JSON.stringify(original));
Limitations:
- ❌ Fails with circular references (throws TypeError)
- ❌ Loses special types like Date, Map, Set, RegExp (converts them to plain objects)
- ❌ Ignores undefined values, functions, and symbols
- ❌ Can be significantly slower than
structuredClone()source
Lodash cloneDeep()
For projects already using Lodash, the cloneDeep() method provides a reliable deep copy solution:
const _ = require('lodash');
const deepCopy = _.cloneDeep(original);
Advantages:
- ✅ Handles circular references
- ✅ Preserves special types and functions
- ✅ Well-tested and widely used
- ✅ Works in all JavaScript environments
Disadvantages:
- ❌ Adds external dependency to your project
- ❌ Larger bundle size compared to native solutions
Custom Deep Copy Function
For specific use cases, you can implement a custom deep copy function:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Array) return obj.map(item => deepClone(item));
const cloned = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
Shallow Copy Methods
For completeness, here are the common shallow copy methods, though they’re not suitable for creating truly independent copies when nested objects are involved:
Spread Operator
const shallowCopy = { ...original };
Object.assign()
const shallowCopy = Object.assign({}, original);
Array Methods (for arrays)
const shallowCopy = original.slice();
const shallowCopy2 = [...original];
Important: These methods only create shallow copies. If your object contains nested objects or arrays, modifying those nested properties in the copy will affect the original object source.
Practical Examples and Best Practices
Basic Object Deep Copy
const user = {
id: 1,
profile: {
name: "Alice",
preferences: {
theme: "dark",
notifications: true
}
},
settings: {
language: "en-US",
timezone: "UTC"
}
};
// Best practice: Use structuredClone()
const userClone = structuredClone(user);
// Now you can safely modify clone
userClone.profile.preferences.theme = "light";
userClone.settings.language = "es-ES";
// Original remains untouched
console.log(user.profile.preferences.theme); // "dark"
console.log(user.settings.language); // "en-US"
Handling Complex Objects
const complexObj = {
date: new Date(),
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
regex: /test/g,
circular: null
};
// Create circular reference
complexObj.circular = complexObj;
// structuredClone handles all this correctly
const clone = structuredClone(complexObj);
console.log(clone.date instanceof Date); // true
console.log(clone.map instanceof Map); // true
console.log(clone.set instanceof Set); // true
console.log(clone.regex instanceof RegExp); // true
console.log(clone.circular === clone); // true (circular reference preserved)
Performance Comparison
Here’s a quick performance comparison for different deep copy methods:
| Method | Speed | Circular References | Special Types | Dependencies |
|---|---|---|---|---|
| structuredClone() | ⭐⭐⭐⭐⭐ | ✅ | ✅ | None |
| JSON.stringify + parse | ⭐⭐ | ❌ | ❌ | None |
| Lodash cloneDeep() | ⭐⭐⭐ | ✅ | ✅ | Lodash |
| Custom function | ⭐⭐⭐ | ✅ | ⭐ (custom) | None |
Performance Considerations
When working with large or deeply nested objects, performance becomes an important consideration:
- structuredClone() is generally the fastest native method for deep copying
- For very large objects, consider if you really need a full deep copy or if selective copying would suffice
- The
structuredClone()method is optimized by JavaScript engines and typically outperforms JSON-based approaches by 10-20% source
Sources
- Deep copy - MDN Web Docs
- Deep Copy vs Shallow Copy in JavaScript - Mastering JS
- Window: structuredClone() method - Web APIs | MDN
- 3 Ways to Copy objects in JavaScript, Shallow vs. Deep Copy - JavaScript Tutorial
- The Power of structuredClone(): A Comprehensive Guide to Deep Cloning in JavaScript
- Deep Cloning in JavaScript: The Modern Way. Use
structuredClone - What is the most efficient way to deep clone an object in JavaScript? - Stack Overflow
- Deep-copying in JavaScript using structuredClone - web.dev
Conclusion
- Use structuredClone() for modern JavaScript deep copying - it’s the most reliable, efficient, and comprehensive solution introduced in 2022
- Avoid shallow copies when you need independent copies of nested objects and arrays
- Consider alternatives like Lodash’s cloneDeep() only if you’re already using Lodash or need compatibility with older browsers
- Test your copies to ensure they’re truly independent by modifying nested properties and verifying the original remains unchanged
- Remember performance when dealing with large objects - structuredClone() is optimized and generally the fastest native option
For your use case with custom literal-constructed objects, structuredClone() provides the perfect balance of simplicity, reliability, and performance for creating truly independent object copies.