๐ก Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:
- Download Node.js to run JavaScript on your computer
- Use your browser's Developer Console (Press F12) to test code snippets
- Create a
.htmlfile with<script>tags and open it in your browser
Deep Copy vs Shallow Copy & Data Structures
Master memory management, reference handling, and data structure copying
What You'll Learn in This Lesson
- โReference vs Value types
- โShallow copying pitfalls
- โDeep copying with structuredClone
- โJSON cloning dangers
- โRecursive copying algorithms
- โImmutable update patterns
Understanding Reference vs Value
Understanding how JavaScript handles data structures, memory, and copying is one of the most important skills for writing bug-free, scalable code. Many of the hardest-to-diagnose bugs come directly from not knowing whether you created a real copy or a shared reference.
JavaScript stores values in two completely different ways: by value and by reference. Primitives such as numbers, strings, booleans, null, undefined, symbol, and bigint are stored by value, meaning each assignment creates a brand-new, independent copy. Objects, arrays, functions, Maps, Sets, and Dates are stored by reference, meaning multiple variables can point to the same memory.
Reference vs Value
Understand how primitives and objects are stored differently
// Primitives are stored by value
let x = 10;
let y = x;
y = 20;
console.log(x); // 10 โ primitive types are fully independent
// Objects are stored by reference
let a = { score: 10 };
let b = a;
b.score = 99;
console.log(a.score); // 99 โ both point to the same reference
// This is why you can break entire applications by accidentally
// modifying the original object when you thought you were modifying a copy๐ก Key Insight: Understanding this distinction lets you debug faster, structure data correctly, and build cleaner architecture.
Shallow Copying: The Hidden Trap
A shallow copy duplicates only the top level of an object. It does not clone nested structures. JavaScript provides several shallow-cloning techniques that developers often assume are deep copies, which leads to subtle bugs.
Shallow Copying
Learn how shallow copies share nested objects
const user = { name: "Leo", prefs: { theme: "dark" } };
const copy = { ...user };
copy.prefs.theme = "light";
console.log(user.prefs.theme);
// "light" โ shallow copies share nested objects
// Common shallow copy operations:
// โข Spread operator { ...obj }
// โข Object.assign({}, obj)
// โข Array methods like .slice(), .concat()
const items = [
{ id: 1, qty: 1 },
{ id: 2, qty: 3 }
];
const shallow = items.slice();
shallow[0].qty = 99;
console.log(items[0].qty);
// 99 โ mutation leaked
...โ ๏ธ Warning: These operations are fast and convenient, but unsafe when dealing with nested structures.
Deep Copying: True Independence
A deep copy creates a fully independent clone, duplicating every level of nesting so changes never leak back into the original structure.
Why deep copies matter:
- When working with complex UI state (React, Vue, Svelte)
- When handling API responses before transforming them
- When storing cached data
- When building undo/redo history
- When preventing reactivity bugs
Deep Copying with structuredClone
Create fully independent clones of nested data
// Modern JavaScript provides structuredClone()
const user = { name: "Leo", prefs: { theme: "dark" } };
const deep = structuredClone(user);
deep.prefs.theme = "light";
console.log(user.prefs.theme);
// "dark" โ deep clone protects the original
// Nested arrays example
const cart = [{ item: "Book", qty: 1 }];
const cloned = structuredClone(cart);
cloned[0].qty = 5;
console.log(cart[0].qty); // 1 โ no mutation
// structuredClone() handles:
// โข Dates, RegExp, Map, Set, ArrayBuffer
// โข Nested
...The JSON Cloning Trap
Many developers use JSON cloning, but it's risky because it destroys important data:
The JSON Cloning Trap
Understand why JSON.parse/stringify destroys data
const user = {
name: "Alex",
joined: new Date(),
greet: () => console.log("Hello"),
score: Infinity,
extra: undefined
};
// JSON cloning destroys data
const cloned = JSON.parse(JSON.stringify(user));
console.log(cloned);
// {
// name: "Alex",
// joined: "2024-01-15T10:30:00.000Z", // Date โ string
// // greet: removed (functions lost)
// score: null, // Infinity โ null
// // extra: removed (undefined lost)
// }
// This makes JSON cloning unsuitable for anything beyond simp
...โ Avoid: JSON cloning destroys Dates, Functions, Infinity/NaN, Undefined, and Circular references.
Building a Recursive Deep Clone
If you want full control, building a recursive deep clone function is the most reliable approach for understanding how cloning actually works under the hood.
Recursive Deep Clone
Build your own deep clone function
function deepClone(value) {
// Handle primitives and null
if (value === null || typeof value !== "object") {
return value;
}
// Handle arrays
if (Array.isArray(value)) {
return value.map(item => deepClone(item));
}
// Handle objects
const clone = {};
for (let key in value) {
if (value.hasOwnProperty(key)) {
clone[key] = deepClone(value[key]);
}
}
return clone;
}
// Test it
const original = {
x: 1,
y: { z: 2 },
arr: [{ id: 1 }, { id: 2 }]
};
...Real-World API Example
Consider this common real-world situation: You fetch user data from an API and want to modify a piece of it for display without altering the original payload.
Real-World API Example
Protect API data from accidental mutation
// โ WRONG: Shallow copy causes silent mutation
const apiData = {
user: {
name: "Eli",
stats: { posts: 20, likes: 88 }
}
};
const displayData = { ...apiData };
displayData.user.stats.likes = 200;
console.log(apiData.user.stats.likes);
// 200 โ silently mutated original data!
// โ
CORRECT: Deep copy protects original
const apiData2 = {
user: {
name: "Eli",
stats: { posts: 20, likes: 88 }
}
};
const clonedData = structuredClone(apiData2);
clonedData.user.stats.likes =
...Working with Maps and Sets
Data structures like Maps and Sets need similar care. Developers often assume copying a Map protects data, but it only copies the key/value references.
Working with Maps and Sets
Deep clone Map and Set data structures
// โ Shallow copy of Map still shares nested objects
const map = new Map();
map.set("profile", { age: 18 });
const copy = new Map(map);
copy.get("profile").age = 40;
console.log(map.get("profile").age);
// 40 โ Map shallow copied, nested object shared
// โ
Deep cloning with structuredClone
const map2 = new Map();
map2.set("profile", { age: 18 });
const deepMap = structuredClone(map2);
deepMap.get("profile").age = 40;
console.log(map2.get("profile").age);
// 18 โ original safe
// Same patt
...Immutable Update Pattern
A powerful real-world pattern is immutable updates, where instead of editing data in place, you clone, change the clone, and return it. This avoids mutation chains and keeps your application predictable.
Immutable Update Pattern
Clone, modify, and return for predictable state
// Immutable update pattern
function updateScore(state, newScore) {
const cloned = structuredClone(state);
cloned.user.score = newScore;
return cloned;
}
const initialState = {
user: { name: "Sam", score: 100 },
settings: { theme: "dark" }
};
const updatedState = updateScore(initialState, 250);
console.log(initialState.user.score); // 100 โ unchanged
console.log(updatedState.user.score); // 250 โ new state
// This pattern is used heavily in:
// โข React state updates
// โข Redux redu
...๐ฏ Pro Pattern: Immutable updates make state changes predictable and prevent cascading mutations across your app.
Performance Considerations
Reference sharing affects performance. Shallow copies are extremely fast because they only duplicate top-level references. Deep copies recursively duplicate all nested data, meaning the cost grows with data complexity.
When to use shallow copying:
- Replacing the outer object only
- Maintaining shared references on purpose
- Avoiding expensive deep-clone performance costs
- Managing lightweight, flat data structures
When to use deep copying:
- Working with nested objects or arrays
- Maintaining immutability in state management
- Preventing unintended mutations
- Building undo/redo systems or time-travel debugging
Performance Considerations
Choose the right copying strategy for your data
// Shallow copy: Fast but risky for nested data
const shallow = { ...bigObject }; // O(n) for top level only
// Deep copy: Slower but safe for nested data
const deep = structuredClone(bigObject); // O(n * depth)
// Choose based on data structure
const flatData = { x: 1, y: 2, z: 3 };
const shallowOK = { ...flatData }; // Safe, fast
const nestedData = {
user: { profile: { settings: { theme: "dark" } } }
};
const deepRequired = structuredClone(nestedData); // Necessary
// Highly nested objec
...Common Mistakes to Avoid
Understanding these common pitfalls will save you hours of debugging:
Common Mistakes to Avoid
Learn the pitfalls that cause mutation bugs
// โ MISTAKE 1: Partial deep copying
const config = {
theme: { mode: "dark" },
languages: ["js", "python"]
};
const clone = {
...config,
languages: [...config.languages] // deep copy array
};
clone.theme.mode = "light";
// Still mutates original โ theme was shallow copied!
// โ
FIX: Deep copy everything consistently
const fixed = structuredClone(config);
fixed.theme.mode = "light";
console.log(config.theme.mode); // "dark" โ
// โ MISTAKE 2: Assuming slice() is safe for nested arra
...Mastery Summary
Mastering deep and shallow copying is essential for writing reliable JavaScript:
- โ Prevents hidden mutations and ghost bugs
- โ Improves debugging and system stability
- โ Keeps UI state consistent across frameworks
- โ Strengthens your mental model of JavaScript memory
- โ Essential for production-level applications
Good developers write code that avoids unintended side effects. Great developers predict them before they happen. Understanding deep vs shallow copying lets you design safer functions, cleaner components, and more stable architectures.
This is one of the most powerful skills for building production-level apps โ and one every serious developer must understand.
Sign up for free to track which lessons you've completed and get learning reminders.