JavaScript Object Deep Clone Techniques Without External Libraries
Cloning objects in JavaScript may seem trivial at first glance, but once nested structures come into play, things can get complicated fast. While shallow copies are straightforward with Object.assign or the spread operator, deep cloning (especially without lodash or another utility library) requires a bit more care. In this blog, we’ll explore three primary ways to deep clone JavaScript objects using only native APIs: structuredClone
, recursion, and JSON.stringify
. We’ll compare each technique, cover their limitations, and even test performance benchmarks so you know when to use which method.
1. Understanding Deep Cloning in JavaScript
A deep clone means fully copying all nested objects within an object to ensure complete data isolation. This is crucial when you want to manipulate the cloned object without affecting the original one, such as in state management, caching strategies, or when duplicating configurations in apps.
Take a basic example:
const original = {
user: {
name: 'Alice',
age: 30
}
};
const shallowCopy = { ...original };
shallowCopy.user.name = 'Bob';
console.log(original.user.name); // 'Bob' — original is affected!
Despite using spread syntax, shallowCopy
still refers to the same nested user
object. That’s why we need deep cloning techniques.
2. Using structuredClone()
—The Native Way
Since ECMAScript 2021, JavaScript offers a built-in method called structuredClone
. It’s the easiest and most accurate deep-cloning solution for most use cases. It can handle complex types like Date, RegExp, ArrayBuffer, and even circular references.
const original = {
user: {
name: 'Alice',
birthdate: new Date(),
preferences: new Map([['theme', 'dark']])
}
};
const copy = structuredClone(original);
console.log(copy);
console.log(copy.user === original.user); // false
Why It Works: structuredClone
performs a full, structured deep clone based on the HTML structured clone algorithm, which is used across browser APIs like postMessage
.
Limitations: While powerful, it’s not universally supported in Node.js versions below 17, and not available in older browsers.
3. Recursive Deep Clone Function
If you’re working in environments without structuredClone
, writing a recursive deep clone function is a reliable alternative. Although more verbose and slightly slower, it gives you full control.
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
Example:
const original = {
name: 'John',
address: {
street: '123 Main St',
city: 'New York'
}
};
const copy = deepClone(original);
copy.address.city = 'Los Angeles';
console.log(original.address.city); // 'New York'
Tips: Extend this function to handle types like Date, Set, or Map if needed. Also, note that this doesn’t protect against circular references unless you track visited nodes.
4. JSON Techniques: The Good and the Bad
The JSON.stringify
/ JSON.parse
combo is a fast and simple deep cloning method—perfect for small, JSON-safe data structures.
const original = {
name: 'Jane',
tags: ['user', 'admin']
};
const copy = JSON.parse(JSON.stringify(original));
console.log(copy);
Pros:
- ✅ Simple to use
- ✅ Fast for small and basic objects
Cons:
- ❌ Fails on circular references
- ❌ Loses functions, undefined values, Symbols, and special objects (e.g. Date)
- ❌ Doesn’t support Map, Set, or custom object prototypes
This makes the JSON method best-suited for plain Object/Array structures—common in data wrangling or simple localStorage scenarios.
5. Performance Comparison
Let’s benchmark each technique with a nested object and analyze the results using console.time
:
const data = {
name: 'Benchmark',
nested: Array.from({ length: 1000 }, (_, i) => ({ id: i, flags: [true, false], meta: { valid: true } }))
};
console.time('structuredClone');
structuredClone(data);
console.timeEnd('structuredClone');
console.time('recursiveClone');
deepClone(data);
console.timeEnd('recursiveClone');
console.time('JSONClone');
JSON.parse(JSON.stringify(data));
console.timeEnd('JSONClone');
Expected Results (runtime varies depending on environment):
- structuredClone: Very fast and reliable for large JSON-safe structures
- recursiveClone: Slightly slower due to recursion overhead
- JSONClone: Fastest for very simple cases, but loses fidelity on non-primitive types
In essence: use structuredClone
whenever available. If working in restricted runtimes or for educational purposes, build your own recursive approach. The JSON-based method is best for lightweight data serialization.
Conclusion
Deep cloning in JavaScript is a vital skill for ensuring data integrity, especially in applications where objects evolve independently. In this post, we explored three key techniques:
- structuredClone: Native, feature-rich, and robust
- Recursive cloning: Flexible and customizable
- JSON parse/stringify: Fast but lossy
Choose the cloning strategy that best suits your application’s complexity and environment. For modern browsers and recent Node.js versions, structuredClone
is your best ally.
Useful links: