A Simple Guide to Shallow Copy vs Deep Copy in JavaScript
When developing in JavaScript, you often need to copy objects. However, simply copying with the = operator can lead to unexpected bugs—because objects are reference types.
In this post, we’ll explore the differences between shallow and deep copies, when to use each, and practical implementation methods you can apply immediately.
What Is a Shallow Copy?
A shallow copy duplicates only the top-level properties of an object. The copied object and the original share references, so if a nested object is modified, it affects the original as well.
How Shallow Copy Works
When a shallow copy is created (o1 !== o2), a new object is generated, but any nested objects or arrays still point to the same memory location.
const original = {
name: "홍길동",
age: 30,
address: {city: "서울",district: "강남구"
}
};
const shallowCopy = Object.assign({}, original);
// Top-level property change – no effect on original
shallowCopy.name = "김철수";
console.log(original.name); // "홍길동"
// Nested object change – affects original!
shallowCopy.address.city = "부산";
console.log(original.address.city); // "부산"
How to Create a Shallow Copy
JavaScript provides several ways to create a shallow copy:
1. Using Object.assign()
const user = {
name: "김민수",
role: "개발자"
};
const clone = Object.assign({}, user);
2. Using Spread Syntax
const user = {
name: "박지영",
role: "디자이너"
};
const clone = { ...user };
3. For Arrays
const items = [1, 2, { value: 3 }];
// Array.from()
const copy1 = Array.from(items);
// slice()
const copy2 = items.slice();
// concat()
const copy3 = [].concat(items);
// Spread syntax
const copy4 = [...items];
Characteristics of Shallow Copy
Reassigning top-level properties in a shallow copy does not affect the original. However, changing nested object properties will affect the original.
const ingredientsList = ["국수", { list: ["계란", "밀가루", "물"] }];
const ingredientsListCopy = Array.from(ingredientsList);
// Change top-level element
ingredientsListCopy[0] = "쌀국수";
console.log(ingredientsList[0]); // "국수" (no effect)
// Change nested object
ingredientsListCopy[1].list = ["쌀가루", "물"];
console.log(ingredientsList[1].list); // ["쌀가루", "물"] (affected!)
What Is a Deep Copy?
A deep copy creates a fully independent clone of an object, including all nested properties. Modifying the copy has no effect on the original object.
Definition of Deep Copy
For two objects to be deeply copied:
- They must be separate objects (
o1 !== o2) - Property names and order must match
- Property values must themselves be deep copies
- The prototype chain must be structurally identical
How Deep Copy Works
A deep copy recursively duplicates all nested objects. Therefore, any changes made to the copy do not affect the original.
const original = {
name: "이수진",
projects: {current: ["프로젝트 A", "프로젝트 B"],completed: ["프로젝트 X"]
}
};
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.projects.current.push("프로젝트 C");
console.log(original.projects.current); // ["프로젝트 A", "프로젝트 B"] (unaffected)
How to Perform a Deep Copy
1. Using JSON.parse(JSON.stringify())
The most common and simple method:
const ingredientsList = ["국수", { list: ["계란", "밀가루", "물"] }];
const ingredientsListDeepCopy = JSON.parse(JSON.stringify(ingredientsList));
ingredientsListDeepCopy[1].list = ["쌀가루", "물"];
console.log(ingredientsList[1].list); // ["계란", "밀가루", "물"] (unaffected)
Pros:
- Easy and intuitive to use
- No additional libraries needed
Cons:
- Functions are not copied
Dateobjects are converted to strings- Special values like
undefined,Symbolare ignored - Errors occur if there are circular references
const obj = {
date: new Date(),
func: () => console.log("hello"),
undef: undefined
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);
// { date: "2024-01-15T10:30:00.000Z" }
// func and undef are removed
2. Using structuredClone()
A built-in JavaScript method:
const original = {
name: "최유진",
date: new Date(),
nested: {data: [1, 2, 3]
}
};
const deepCopy = structuredClone(original);
deepCopy.nested.data.push(4);
console.log(original.nested.data); // [1, 2, 3] (unaffected)
console.log(deepCopy.date instanceof Date); // true
Pros:
- Correctly handles
Date,RegExp,Map,Set, etc. - Supports circular references
- Good performance
Cons:
- Still does not copy functions
- May not be supported in older browsers
3. Custom Recursive Function
Use this when full control is needed:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
const cloneObj = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj);
for (let key in obj) {if (obj.hasOwnProperty(key)) { cloneObj[key] = deepClone(obj[key], hash);}
}
return cloneObj;
}
const original = {
name: "정민호",
skills: ["JavaScript", "React"],
experience: {years: 5,companies: ["A사", "B사"]
}
};
const copy = deepClone(original);
copy.skills.push("TypeScript");
console.log(original.skills); // ["JavaScript", "React"] (unaffected)
Pros:
- Fully customizable
- Can handle special types as needed
Cons:
- More complex to implement and maintain
- Must handle all edge cases
4. Using Lodash Library
lodash is a popular utility library in JavaScript.
import _ from 'lodash';
const original = {
name: "강서연",
metadata: {tags: ["frontend", "react"],createdAt: new Date()
}
};
const deepCopy = _.cloneDeep(original);
Pros:
- Reliable, production-tested implementation
- Handles most edge cases well
Cons:
- Requires an external library
- Adds to bundle size (though not significant for Lodash)
When to Use Shallow Copy
Shallow copying is suitable for:
1. Performance-Critical Scenarios
If you don’t need to modify nested objects, shallow copying is faster and more memory-efficient.
const userConfig = {
theme: "dark",
language: "ko"
};
// Only changing top-level properties
const newConfig = { ...userConfig, theme: "light" };
2. Changing Only Top-Level Properties
If nested objects remain untouched, shallow copy suffices.
const product = {
id: 1,
name: "노트북",
price: 1500000
};
const updatedProduct = { ...product, price: 1400000 };
3. Passing Props in React
React often uses shallow copies to maintain immutability.
const handleUpdate = (newData) => {
setUser({ ...user, ...newData });
};
When to Use Deep Copy
Deep copying is essential when:
1. You Need Fully Independent Copies
Use when you need objects fully decoupled from the original.
const template = {
title: "기본 템플릿",
sections: [{ type: "header", content: "제목" },{ type: "body", content: "본문" }
]
};
const userTemplate = structuredClone(template);
userTemplate.sections[0].content = "사용자 제목";
2. Modifying Nested Structures
Deep copy is crucial for handling complex data structures.
const projectData = {
name: "신규 프로젝트",
team: {frontend: ["김개발", "이디자인"],backend: ["박서버", "최디비"]
}
};
const archivedProject = structuredClone(projectData);
archivedProject.team.frontend.push("신입사원");
// Original remains unaffected
3. Managing State History
Useful for implementing undo/redo functionality.
const history = [];
function saveState(currentState) {
history.push(structuredClone(currentState));
}
function undo() {
return history.pop();
}
Performance Considerations
Deep and shallow copies differ significantly in performance.
Shallow Copy Performance
Very fast since only top-level properties are copied. Benefits increase with object size.
// Fast
const shallowCopy = { ...largeObject };
Deep Copy Performance
Slower due to traversing and copying every nested structure.
// Slower
const deepCopy = structuredClone(largeObject);
Optimization Tips
When working with large objects:
- Copy Only What You Need
const { metadata, ...essentialData } = largeObject;
const copy = structuredClone(essentialData);
- Use Memoization
const cache = new WeakMap();
function getCachedCopy(obj) {
if (!cache.has(obj)) {cache.set(obj, structuredClone(obj));
}
return cache.get(obj);
}
- Choose the Right Method
- Simple objects:
JSON.parse(JSON.stringify()) - Complex objects:
structuredClone() - Custom logic: Manual implementation or Lodash
FAQ
Q: Are arrays copied the same way as objects?
Yes, arrays follow the same principles. Use spread or slice() for shallow copies, and structuredClone() for deep copies.
Q: What copy method is commonly used in React?
React typically uses shallow copies. For example, { ...state } is often used to maintain immutability during state updates.
Q: Isn’t JSON deep copy good enough?
It works for simple data. But if you need to handle Date, functions, or undefined, use structuredClone() or Lodash instead!