1. 원시 값(Primitive)
string,number,boolean,null,undefined,symbol,BigInt- 값 자체를 저장하며, 변경 불가능(Immutable)
- === 비교 시 값만 비교 (
10 === 10 → true)- 메모리: 스택(Stack)에 저장
** 2. 객체(Object)**
{},[],function() {},new Date(),new Map()- 참조 타입, 변경 가능(Mutable)
- === 비교 시 메모리 주소(참조) 비교 (
{ a: 1 } !== { a: 1 })- 메모리: 힙(Heap)에 저장, 변수는 참조(Reference) 저장
| 구분 | 원시 값 (Primitive) | 객체 (Object) |
|---|---|---|
| 저장 방식 | 값 자체를 저장 | 참조(Reference) 저장 |
| 메모리 위치 | 스택(Stack) | 힙(Heap) |
| 비교 방식 | 값 자체 비교 (===) | 참조(메모리 주소) 비교 (===) |
| 변경 가능 여부 | 불변(Immutable) | 변경 가능(Mutable) |
| 예제 | let a = 10; | let obj = { name: "Alice" }; |
- 원시값은 변수에 할당 시, 변수(확보된 메모리 공간)에는 실제 값이 저장됨.
- 객체를 변수에 할당 시, 변수(확보된 메모리 공간)에는 참조 값이 저장됨.
- 객체를 변수에 저장하면, 해당 변수에는 **객체의 값이 아닌 참조(메모리 주소)**가 저장됨.
- 객체는 같은 메모리 주소를 공유하므로, 하나를 변경하면 다른 변수도 영향을 받음.
let obj1 = { name: "Alice" };
let obj2 = obj1; // 같은 객체를 참조
obj2.name = "Bob"; // obj2에서 변경하면 obj1도 영향을 받음
console.log(obj1.name); // "Bob"
console.log(obj2.name); // "Bob"- 함수에 객체를 전달하면, 객체의 참조(메모리 주소)가 전달되므로 함수 내부에서 변경하면 원본 객체도 변경됨.
- 객체가 함수의 매개변수로 전달되면, 새로운 복사본이 아니라 같은 객체를 참조함.
function update(obj) {
obj.name = "Charlie";
}
let user = { name: "Alice" };
update(user);
console.log(user.name); // "Charlie" (원본 객체가 변경됨)객체의 1단계 속성(직접 포함된 속성)만 복사하고, 중첩된 객체(하위 객체)는 참조만 복사하는 방식
방법
- Object.assign()
const obj1 = { name: "Alice", address: { city: "Seoul" } };
const obj2 = Object.assign({}, obj1);
obj2.name = "Bob"; // 기본 속성은 복사됨 (독립적)
obj2.address.city = "Busan"; // 중첩 객체는 참조 복사됨
console.log(obj1.name); // "Alice" (영향 없음)
console.log(obj2.name); // "Bob"
console.log(obj1.address.city); // "Busan" (중첩 객체가 변경됨!)- 스프레드 연산자 { ...obj }
const obj1 = { name: "Alice", address: { city: "Seoul" } };
const obj2 = { ...obj1 };
obj2.name = "Bob"; // 기본 속성은 복사됨 (독립적)
obj2.address.city = "Busan"; // 중첩 객체는 참조 복사됨
console.log(obj1.address.city); // "Busan" (중첩 객체가 변경됨)📌 얕은 복사의 문제점 • 1단계 속성(Primitive Value)은 복사됨 → 독립적으로 변경 가능. • 중첩 객체(하위 객체)는 참조만 복사됨 → 변경 시 원본도 영향을 받음. • 즉, 중첩된 객체를 변경하면 원본도 변경되는 문제 발생!
객체 내부의 모든 값(중첩 객체 포함)을 새로운 메모리에 복사하여 독립적인 객체 만드는 방식
방법 1. JSON 방식 (JSON.parse(JSON.stringify(obj))) ✅ 모든 객체가 새로 복사됨 → 원본 객체에 영향을 주지 않음. ❌ 단점: JSON.stringify()는 함수, undefined, Symbol 같은 값은 제거됨!
const obj1 = { name: "Alice", address: { city: "Seoul" } };
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.name = "Bob"; // 기본 속성은 복사됨
obj2.address.city = "Busan"; // 중첩 객체도 독립적으로 변경 가능
console.log(obj1.address.city); // "Seoul" (원본 유지됨!)
console.log(obj2.address.city); // "Busan"2. lodash 라이브러리 (_.cloneDeep(obj)) ✅ lodash의 _.cloneDeep()을 사용하면 함수, undefined도 포함한 완전한 깊은 복사가 가능!
const _ = require("lodash");
const obj1 = { name: "Alice", address: { city: "Seoul" } };
const obj2 = _.cloneDeep(obj1);
obj2.address.city = "Busan";
console.log(obj1.address.city); // "Seoul" (원본 유지됨)
console.log(obj2.address.city); // "Busan"3. 구조적 복사 (structuredClone(obj), 최신 브라우저 지원) ✅ 최신 브라우저에서 네이티브 깊은 복사 지원! ❌ 함수(function)는 복사되지 않음!
const obj1 = {
name: "Alice",
address: { city: "Seoul" },
greet: () => "Hello",
};
const obj2 = structuredClone(obj1);
obj2.address.city = "Busan";
console.log(obj1.address.city); // "Seoul" (원본 유지됨)
console.log(obj2.address.city); // "Busan"| 복사 방식 | 기본 속성 복사 | 중첩 객체 복사 | 원본 영향 여부 | 사용 방법 |
|---|---|---|---|---|
| 얕은 복사 | ✅ 독립적 복사 | ❌ 참조만 복사 | 🛑 원본 영향 받음 | Object.assign(), { ...obj } |
| 깊은 복사 | ✅ 독립적 복사 | ✅ 완전한 복사 | ✅ 원본 영향 없음 | JSON.parse(JSON.stringify(obj)), _.cloneDeep(), structuredClone() |