
Shallow vs Deep Cloning Objects in JavaScript
In JavaScript, primitive values (Number, String, Boolean) are copied by value, whereas objects and arrays are assigned by reference. Even when developers reach for Object.assign({}, obj) or the ES2018 object spread syntax ({ ...obj }), nested child objects still share the exact same memory reference—leading to unexpected state mutations in frontend applications.
Example
var original = { name: "Ahmed", preferences: { theme: "dark" } };
var shallow = Object.assign({}, original);
shallow.preferences.theme = "light";
console.log(original.preferences.theme);
// Output: "light" (original was mutated because preferences is nested!)
Input : Nested JavaScript object containing arrays, Dates, and sub-objects
Output : Independent deep clone with zero shared nested references.
Implementation
function deepClone(value) {
// Primitives and null are returned directly
if (value === null || typeof value !== "object") {
return value;
}
// Handle Date instances
if (value instanceof Date) {
return new Date(value.getTime());
}
// Handle Arrays recursively
if (Array.isArray(value)) {
return value.map(function (item) {
return deepClone(item);
});
}
// Handle plain Objects recursively
var copy = {};
Object.keys(value).forEach(function (key) {
copy[key] = deepClone(value[key]);
});
return copy;
}
// Verification
var state = {
user: "ahmd",
tags: ["html", "css"],
settings: { notifications: true }
};
var nextState = deepClone(state);
nextState.settings.notifications = false;
nextState.tags.push("javascript");
console.log(state.settings.notifications); // true (untouched)
console.log(state.tags.length); // 2 (untouched)
Main Point: Use { ...obj } or Array.prototype.slice() when your data structure is guaranteed to be flat (one level deep), and use a recursive deepClone helper whenever mutating nested state trees.
Credits
Photo by Alexandre Debieve