
Deep‑Cloning vs. Shallow‑Cloning in JavaScript

When working with objects in JavaScript, we often need to create copies. However, not all copies behave the same way. Some methods copy only the top‑level properties, whilst others create a fully independent duplicate. If we do not use the right approach, we can run into unintended side effects where changing one object also changes another.
This is where shallow cloning and deep cloning come into play. In this article, I explore how they work, the best methods for each, and when to use them to avoid unnecessary bugs.
What is Shallow‑Cloning?
A shallow clone creates a new object with copies of the original's top‑level properties. However, if the object contains nested objects or arrays, those remain referenced rather than copied. This means changes to nested structures affect both the original and the clone.
An Example of Shallow‑Cloning
The spread operator (...) is a common way to create a shallow clone of an object:
const original = { name: "Ellie", details: { age: 30 } };const shallowClone = { ...original };shallowClone.name = "Bob";shallowClone.details.age = 40;console.log(original.name); // "Ellie" (unchanged)console.log(original.details.age); // 40 (changed)Here, shallowClone.name is independent of the original; however, shallowClone.details still points to the same object as original.details. This means modifying shallowClone.details.age also updates original.details.age.
Other Ways to Create a Shallow Clone
Apart from the spread operator, we can also do something like this:
const shallowClone1 = Object.assign({}, original);const shallowClone2 = structuredClone(original, { transfer: [] }); // For transferable objectsBoth methods behave similarly, copying only the first level of properties whilst keeping references to nested objects.
When Should We Use Shallow Cloning?
Shallow cloning is ideal when:
- The object only contains primitive values.
- We need a quick copy without duplicating deeply nested structures.
- The nested objects should remain shared between copies.
If we need a completely separate object, including its nested properties, we need a deep clone.
What is Deep‑Cloning?
A deep clone creates a fully independent copy of an object, including all nested objects and arrays. Changes to the cloned object do not affect the original, and vice versa.
An Example of Deep‑Cloning
const original = { name: "Ellie", details: { age: 30 } };const deepClone = JSON.parse(JSON.stringify(original));deepClone.details.age = 40;console.log(original.details.age); // 30 (unchanged)Since we have created a new structure, modifying deepClone.details.age does not affect original.details.age.
Better Ways to Deep‑Clone Objects
Whilst JSON.parse(JSON.stringify(...)) works, it has limitations:
- It does not copy functions, Dates, Maps, or Sets.
- It ignores
undefinedvalues.
A better approach is using structured cloning or a deep‑cloning utility like Lodash's cloneDeep.
Using structuredClone (Modern JavaScript Method)
const deepClone = structuredClone(original);This method correctly handles most data types and is natively supported in modern browsers.
Using Lodash's cloneDeep
In situations where you might need to support browsers that pre‑date structuredClone, Lodash offers an easy‑to‑use method to achieve much the same:
import cloneDeep from "lodash.clonedeep";const deepClone = cloneDeep(original);This works by recursively copying all properties of an object, ensuring that nested structures, arrays, and special types like Date, Map, and Set are fully duplicated, rather than referenced. This makes it the most reliable deep‑cloning method in JavaScript, and avoids issues found in simpler approaches like JSON.parse(JSON.stringify(obj)), or potential browser incompatibility with structuredClone.
Performance Considerations
Deep cloning is more computationally expensive than shallow cloning, as it creates entirely new copies of nested structures. If an object is large or deeply nested, this can lead to significant performance overhead.
Shallow vs. Deep Cloning Performance
We can see the performance differences very easily by setting up a simple test:
const obj = { nested: { value: 42 } };console.time("Shallow Clone");for (let i = 0; i < 1000000; i++) { const clone = { ...obj };}console.timeEnd("Shallow Clone");console.time("Deep Clone");for (let i = 0; i < 1000000; i++) { const clone = structuredClone(obj);}console.timeEnd("Deep Clone");For small objects, the performance difference is minor. However, deep cloning can become significantly slower as objects grow larger.
When Should We Use Deep Cloning?
Deep cloning is useful when:
- We need completely independent objects to avoid unwanted mutations.
- The object contains nested structures that should not be shared.
- We are working with immutable state updates in libraries like Redux.
For simple structures or when performance is a concern, shallow cloning is often a better choice.
Wrapping up
Choosing between shallow and deep cloning depends on what we are trying to achieve. If we only need a copy of top‑level properties, shallow cloning is fast and efficient. However, if we need to ensure that nested structures are fully independent, deep cloning is the safer approach.
Key Takeaways
Shallow cloning
copies top‑level properties but keeps references to nested objects.Deep cloning
creates a fully independent copy of an object, including its nested structures.The spread operator (
...) andObject.assign()perform shallow cloning.
and Lodash'sstructuredClone()cloneDeep()are the most reliable methods for deep cloning.Deep cloning has performance costs
, so it should only be used when necessary.
Understanding how these cloning methods work helps us write more predictable and bug‑free code, making our applications easier to maintain.
Related Articles

Mastering JavaScript Iterators and Generators. 
Object.assign() in JavaScript: Merging and Shallow Copies. Object.assign()in JavaScript: Merging and Shallow Copies
Breadth‑First Search: Solving Binary Tree Level Order Traversal. Breadth‑First Search: Solving Binary Tree Level Order Traversal

Horizontal & Vertical Scanning: The Longest Common Prefix Problem. Horizontal & Vertical Scanning: The Longest Common Prefix Problem

Leveraging JavaScript Frameworks for Efficient Development. Leveraging JavaScript Frameworks for Efficient Development
How to Check an Element Exists with and Without jQuery. How to Check an Element Exists with and Without jQuery

Understanding Tail call Optimisation in JavaScript. Understanding Tail call Optimisation in JavaScript

How to Use and Clear the CSS float Property. How to Use and Clear the CSS
floatProperty
Automatically Generate Text Sitemaps in Gatsby. Automatically Generate Text Sitemaps in Gatsby

When to Use var or let or const. When to Use
varorletorconst
The Palindrome Number Problem: Strings vs. Maths in JavaScript. The Palindrome Number Problem: Strings vs. Maths in JavaScript

How to Prevent Race Conditions in JavaScript with AbortController. How to Prevent Race Conditions in JavaScript with
AbortController