Shallow Clone vs Deep Clone in JavaScript
Understanding object cloning and immutability concepts
Shallow Clone vs Deep Clone
In this document, I want to explain the difference between shallow clone and deep clone in JavaScript, why cloning matters, and how to perform it correctly with code examples.
1. Shallow Clone
A shallow clone copies only the first level of properties. Nested objects and arrays are not duplicated — instead, their references are copied. This means the original and the copy still point to the same nested structures.
Example: Object Spread
const obj = {
name: "Hank",
address: { city: "Leeds" },
};
const copy = { ...obj }; // Shallow clone
copy.name = "Kim";
console.log(obj.name); // "Hank" ✅ unaffected
copy.address.city = "London";
console.log(obj.address.city); // "London" ❌ original changed
Example: Object.assign
const obj = { name: "Hank", address: { city: "Leeds" } };
const copy = Object.assign({}, obj);
copy.address.city = "London";
console.log(obj.address.city); // "London" ❌ still changes original
👉 Shallow clone works for top-level values but does not protect nested objects or arrays.
2. Deep Clone
A deep clone duplicates the entire structure, including nested objects and arrays. The result is completely independent of the original object.
Example: structuredClone
(recommended)
const obj = {
name: "Hank",
address: { city: "Leeds" },
};
const deepCopy = structuredClone(obj);
deepCopy.address.city = "London";
console.log(obj.address.city); // "Leeds" ✅ safe
Example: Nested Array
const arr = [
[1, 2],
[3, 4],
];
const deepCopy = structuredClone(arr);
deepCopy[0][0] = 99;
console.log(arr[0][0]); // 1 ✅ unaffected
3. Why It Matters
-
State Management and Immutability
- In React or Redux, state must not be mutated directly.
- Shallow clone may share references, causing unintended mutations.
- Deep clone ensures a new, independent object for safe state updates.
-
Data Integrity
- Prevents accidental changes to original data when transformations are needed.
- Useful when you want to keep server responses intact but create derived versions.
-
Bug Prevention
- Avoids subtle bugs caused by shared references in deeply nested structures.
4. Deep Clone Methods
1. structuredClone
(Modern API)
const original = {
user: { name: "Hank", info: { age: 25 } },
};
const clone = structuredClone(original);
clone.user.info.age = 30;
console.log(original.user.info.age); // 25 ✅
- Supports most built-in types (Objects, Arrays, Dates, Maps, Sets).
- Fast and safe.
- Not available in very old environments.
2. JSON Serialization (Classic Trick)
const obj = { name: "Hank", date: new Date() };
const clone = JSON.parse(JSON.stringify(obj));
console.log(clone.name); // "Hank"
console.log(clone.date); // string, not Date ❌
-
Limitations:
- Cannot clone functions
- Loses
Date
,Map
,Set
,undefined
, circular references
3. Manual Recursive Clone (Custom Implementation)
function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (Array.isArray(obj)) {
return obj.map((item) => deepClone(item));
}
const clone = {};
for (const key in obj) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
const original = { user: { name: "Hank", hobbies: ["coding", "music"] } };
const copy = deepClone(original);
copy.user.hobbies[0] = "travel";
console.log(original.user.hobbies[0]); // "coding" ✅