오늘은 깊은 복사의 구현 방법에 대해 공부하다가, Object.assign 메서드에 대해서도 알게 되었습니다.
먼저 Object.assign을 간단히 정리하고, 해당 메서드로도 구현할 수 없는 깊은 복사를
재귀함수로 구현하는 방법을 알아보려고 합니다.
Object.assign
Object.assign
메서드는 소스 객체의 모든 열거 가능한 속성을 타겟 객체에 복사합니다.
이 메서드는 타겟 객체를 수정하고, 수정된 타겟 객체를 반환합니다.Object.assign
은 주로 객체의 속성을 복사하거나 합치는 데 사용됩니다.
사용법
Object.assign
메서드는 첫 번째 인자로 타겟 객체를 받고, 그 뒤에 하나 이상의 소스 객체를 받습니다.
소스 객체의 속성은 타겟 객체로 복사되며, 이미 존재하는 속성은 덮어쓰기 됩니다.
Object.assign(target, ...sources);
예제 코드
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target); // { a: 1, b: 4, c: 5 }, 타겟 객체가 수정됩니다.
console.log(returnedTarget); // { a: 1, b: 4, c: 5 }, 반환된 객체도 동일합니다.
// 여러 소스 객체
const first = { a: 1 };
const second = { b: 2 };
const third = { c: 3 };
const result = Object.assign({}, first, second, third);
console.log(result); // { a: 1, b: 2, c: 3 }
// 배열도 처리 가능하지만 배열을 객체처럼 다룹니다.
const obj = { a: 1 };
const array = [2, 3];
const arrayToObject = Object.assign(obj, array);
console.log(arrayToObject); // { '0': 2, '1': 3, a: 1 }
주의사항
1) 얕은 복사 (Shallow Copy): Object.assign
은 객체 내부의 객체(중첩된 객체)를 깊은 복사(deep copy)하지 않고 얕은 복사(shallow copy)를 수행합니다. 이로 인해 내부 객체는 여전히 참조로 연결되어 있어서 복사된 객체와 원본 객체가 내부 객체를 공유하게 됩니다.
2) 열거 불가능한 속성: Object.assign
은 열거 가능한 속성만 복사합니다. Object.defineProperty
등을 사용해 열거 불가능하게 설정된 속성은 복사되지 않습니다.
언제 사용해야 하나요?
Object.assign
은 주로 객체의 속성을 확장하거나 기본 객체에 새로운 속성을 추가할 때 유용합니다.
설정 객체나 옵션 객체를 다룰 때 특히 유용하며, 간단한 상속을 구현할 때도 사용할 수 있습니다.
그러나 깊은 복사가 필요한 경우에는 다른 방법을 사용해야 합니다.
깊은 복사, 재귀함수
그렇다면 깊은 복사를(라이브러리를 사용하지 않고)간단히 구현해볼 수 있는 방법이 없을까요?
아직은 조금 어렵게 느껴지지만 재귀함수의 사용을 고려해 볼 수 있을 것 같습니다.
그리고, JSON.stringify 와 JSON.parse 를 반복하는 방법으로도 가능한 것으로 알고 있어서 정리해 보겠습니다.
재귀 함수를 사용한 깊은 복사
재귀 함수를 사용하여 깊은 복사를 수행하는 것은 중첩된 객체나 배열 등을 완벽하게 복사할 수 있도록 해줍니다.
이 방법은 각 속성의 타입을 확인하고, 객체나 배열일 경우 해당 함수를 다시 호출하여 각 속성을 복사하는 방식으로 작동합니다.
function deepCopy(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
let temp = obj instanceof Array ? [] : {};
for (let key in obj) {
temp[key] = deepCopy(obj[key]);
}
return temp;
}
// 예제
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const copied = deepCopy(original);
console.log(copied); // { a: 1, b: { c: 2, d: [3, 4] } }
다른 방법: JSON 메소드 사용
위에서 언급한 다른 방법으로 JSON 메소드를 사용할 수 있다고 합니다.JSON.stringify()
로 객체를 문자열로 변환한 뒤, JSON.parse()
로 다시 객체로 변환합니다.
이 방법은 실행이 간단하고 빠르지만,
함수, Date
객체, undefined
, Infinity
와 같은 일부 데이터 타입이나 순환 참조를 처리하지 못합니다.
const original = { a: 1, b: { c: 2, d: new Date() } };
const copied = JSON.parse(JSON.stringify(original));
console.log(copied); // { a: 1, b: { c: 2, d: "2020-05-20T12:34:56.789Z" } }
언제 어떤 방법을 사용해야 할까?
- 재귀 함수 사용: 모든 종류의 데이터 타입을 처리하고, 순환 참조를 처리할 필요가 있을 때 적합합니다. 다만, 구현이 조금 더 복잡하며, 깊은 중첩이나 큰 객체에서 스택 오버플로우가 발생할 수 있습니다.
- JSON 메소드 사용: 코드가 단순하고 빠르게 처리할 수 있지만, 일부 데이터 타입을 제대로 복사하지 못하고 순환 참조를 처리할 수 없다는 단점이 있습니다.
결론
재귀 함수를 사용하거나 JSON 메서드를 사용해서 깊은 복사를 구현할 수 있습니다.
하지만 lodash 등 라이브러리 사용도 고려해보는 것이 좋을 것 같습니다.
아무래도 널리 사용되어서 이미 안정성과 성능이 보장된 라이브러리이기 때문일 것입니다!
'javascript' 카테고리의 다른 글
[240512 WIL] Hash Routing, History API (0) | 2024.05.12 |
---|---|
[240510 TIL] 이벤트 버블링, 캡처링, 관련메소드 (0) | 2024.05.10 |
[240507 TIL] Object.is (0) | 2024.05.07 |
[240504 WIL] query params (0) | 2024.05.04 |
[240503 TIL] 전역변수? 지역변수? live binding (0) | 2024.05.03 |