
자바스크립트의 객체 복사에는 깊은 복사와 얕은 복사 두 가지가 있다.
얕은 복사는 객체의 실제 값이 아닌 참조값만 복사하는 방식이다. 즉, 객체의 최상위 레벨 속성만 복사하고 그 안에 중첩된 객체가 있으면 그건 복사하지 못하고 참조값을 그대로 공유하게 된다.
const obj1 = {
name: "Gildong",
age: 24
};
const obj2 = obj1;
얕은 복사는 이와 같이 할당연산자를 통해서 참조값을 그대로 넣어주면 된다.
그러나 얕은 복사를 할 경우 객체를 수정했을 때 두 객체가 같은 참조값을 가리키기 때문에 하나를 수정하면 다른 것도 함께 수정되는 불상사가 발생한다.
const obj1 = {
name: "Gildong",
age: 24
};
const obj2 = obj1;
obj2.name = "Jiwon";
console.log(obj1.name); // Jiwon
console.log(obj2.name); // Jiwon
위의 코드에서 원하는 것은 obj2의 이름 수정이었지만 얕은 복사로 인해 두 객체가 같은 참조값을 공유하기 때문에 obj1의 name 역시 Jiwon이 된다.
이를 방지하기 위해서 우리는 깊은 복사를 수행한다.
깊은 복사는 객체의 모든 값을 복사해서 완전히 독립적인 새로운 객체를 만든다. 즉, 객체 내부의 모든 값을 하나하나씩 복사하는 것을 의미한다.
구체적으로 깊은 복사의 조건을 살펴보면 다음과 같다.
- 두 객체는 같은 객체가 아니다.
- obj1과 obj2의 속성은 같은 이름과 순서이다.
- 두 객체의 속성 값은 서로의 깊은 복사 값이다.
- 두 객체의 프로토타입 체인은 구조적으로 동일하다.
깊은 복사를 직접 구현하려면 재귀적으로 속성을 하나씩 복사하는 방법이 있다.
이와 같이 재귀적으로 복사를 수행하면서 중첩된 객체 역시 완벽하게 복사할 수 있다.
그러나 이러한 코드를 매번 구현하는 것은 매우 번거롭고 복잡하다. 또한, 이는 순환 참조 처리가 어렵다.
let copyObjectDeep = function(target) {
let result = {};
if (typeof target === 'object' && target !== null) {
for (let prop in target) {
result[prop] = copyObjectDeep(target[prop]);
}
} else {
result = target;
}
return result;
}
이 방법은 객체를 JSON 문자열로 변환한 다음 다시 객체로 변환해서 깊은 복사를 하는 방식이다.
const obj1 = {
x: 5,
y: 6
};
const obj2 = JSON.stringify(obj1);
이 방법을 통해 간단하게 깊은 복사를 수행할 수 있다.
그러나, 함수, undefined, 심볼과 같은 값은 생략되거나 null로 변환된다. 다시 말하자면, 일부는 제대로 복사가 되지 않을 수 있다.
이 방법은 이번에 처음 알게 됐는데 팀원분이 알려주셨ㄷ ㅏ...
사용법도 매우 간단하며 JSON.stringify보다 지원되는 데이터타입이 더 많다.
const original = { a: 1, b: { c: 2 } };
const deepCopy = structuredClone(original);
그러나, 이 역시도 Function, Error, Proxy, WeakMap, WeakSet 등의 데이터 타입은 복사가 지원되지 않는다.