Deep Copy vs Shallow Copy in JavaScript
In JavaScript, when you copy an object or array, you might either perform a shallow copy or a deep copy. Understanding the difference is crucial, especially when dealing with complex data structures.
Memory and References Basics
JavaScript objects and arrays are reference types. When you assign an object or array to a variable, you're assigning a reference to its memory location, not the actual data.
-
Primitive types (e.g.,
string
,number
,boolean
) are stored by value. -
Non-primitive types (e.g.,
object
,array
,function
) are stored by reference.
Shallow Copy
A shallow copy duplicates the top-level properties but references any nested objects or arrays. Changes to the nested objects in the copy affect the original.
How It Works
- Copies only the first level of the object/array.
- Nested objects/arrays share the same reference.
Pitfall
When you modify a nested object in the copy, it also modifies the original object because they share the same memory reference.
Examples
1. Shallow Copy with Arrays
const arr = [1, 2, [3, 4]];
const shallowCopy = arr.slice(); // or [...arr]
shallowCopy[0] = 100;
console.log(arr); // [1, 2, [3, 4]] -> top-level unaffected
shallowCopy[2][0] = 999;
console.log(arr); // [1, 2, [999, 4]] -> nested array modified
2. Shallow Copy with Objects
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj };
shallowCopy.a = 100;
console.log(obj); // { a: 1, b: { c: 2 } } -> top-level unaffected
shallowCopy.b.c = 999;
console.log(obj); // { a: 1, b: { c: 999 } } -> nested object modified
Key Takeaway
- Top-level properties are copied by value.
- Nested structures are copied by reference.
Shallow Copy Methods
-
Arrays:
Array.prototype.slice()
,spread operator ([...arr])
. -
Objects:
Object.assign()
,spread operator ({...obj})
.
Deep Copy
A deep copy creates a new object or array with completely independent copies of the original structure, including all nested levels.
How It Works
- Recursively copies all levels of an object or array.
- Nested objects/arrays get independent copies.
Safe from Mutation
Changes in the copied object or array do not affect the original.
Examples
1. Deep Copy with structuredClone()
const arr = [1, 2, [3, 4]];
const deepCopy = structuredClone(arr);
deepCopy[2][0] = 999;
console.log(arr); // [1, 2, [3, 4]] -> original remains intact
console.log(deepCopy); // [1, 2, [999, 4]]
2. Deep Copy with JSON.parse(JSON.stringify())
const obj = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(obj));
deepCopy.b.c = 999;
console.log(obj); // { a: 1, b: { c: 2 } } -> original intact
console.log(deepCopy); // { a: 1, b: { c: 999 } }
Limitations of JSON.parse(JSON.stringify())
-
Loses functions and
undefined
values. -
Doesn't handle
Date
,RegExp
,Map
,Set
,BigInt
, orSymbol
.
Key Differences
Aspect | Shallow Copy | Deep Copy |
---|---|---|
Copy Depth | Only the first level | All nested levels |
Nested Changes | Affect the original object | Do not affect original |
Performance | Faster for small objects | Slower due to recursion |
Complexity | Simple and native methods | Complex with recursion |
Methods |
spread , slice , Object.assign
|
structuredClone , JSON.parse(JSON.stringify())
|
When to Use What?
-
Shallow Copy:
- When you need to copy simple objects with no nested structure.
- When performance is more critical than integrity of nested data.
-
Deep Copy:
- When working with complex nested objects.
- When the copied object needs complete independence.
In-Depth Mechanics
Shallow Copy Mechanics
- Create a new object/array.
- Copy primitive values by value.
- Copy objects/arrays by reference.
Example:
const obj = { x: 1, y: { z: 2 } };
const shallow = { ...obj };
Memory layout:
-
shallow.x
: New primitive copy. -
shallow.y
: Same reference asobj.y
.
Deep Copy Mechanics
- Recursively iterate through each property.
- Copy primitive values by value.
- Recursively copy objects/arrays by creating new instances.
Example:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
const newObj = {};
for (let key in obj) {
newObj[key] = deepClone(obj[key]);
}
return newObj;
}
Memory layout:
- Every level is independently copied.
Performance Consideration
Deep copies take more time and resources compared to shallow copies due to the recursive nature of traversing nested structures.
Benchmark:
console.time('shallow');
const shallowCopy = [...Array(100000).keys()];
console.timeEnd('shallow');
console.time('deep');
const deepCopy = JSON.parse(JSON.stringify(Array(100000).fill({ x: 1 })));
console.timeEnd('deep');
Conclusion
- Shallow Copy: Efficient but risky with nested objects.
- Deep Copy: Safe but more resource-intensive.
For modern applications, structuredClone()
is recommended for deep copies, as it avoids many limitations of JSON.parse(JSON.stringify())
.
Top comments (0)