이번 포스팅에서 알아볼 내용은 JavaScript의
얕은 복사
깊은 복사
에 대해 알아보겠습니다.
우선 JavaScript의 자료형은 원시값과 참조값으로 나뉩니다.
원시값
- Number
- String
- Boolean
- Null
- Undefined
참조값
- Object
- Symbol
원시 값은 값을 복사할 때 복사된 값을 다른 메모리에 할당하기 때문에
복사한 값과 복사된 값이 서로에게 영향을 미치지 않습니다.
// 원시값 복사 => 기존의 값에 영향을 미치지 않는다
const num = 1
let num2 = num
num2 = 2
// 1
console.log(num)
// 2
console.log(num2)
하지만 참조값은 변수가 객체의 주소를 가리키는 값이기 때문에 복사된 값(주소)이 같은 값을 가리킵니다.
// 참조값 복사 => 기존의 값에 복사된 값이 영향을 미친다
const num = {
number : 1
}
let num2 = num
num2.number = 2
// {number: 3}
console.log(num)
// {number: 3}
console.log(num2)
이러한 참조형의 특징 때문에 참조형을 복사하는 방법은 얕은 복사 방법과 깊은 복사 방법 두 가지로 나뉩니다.
얕은 복사 ( Shallow Copy )
얕은 복사는 객체(참조형)를 복사할 때 기존의 값과 복사된 값이 같은 메모리 주소를 가리키고 있는 것을 말합니다.
객체 안에 객체가 있는 경우 한 개의 객체라도 원본의 객체를 참조하고 있다면 이것을 얕은 복사라고 합니다.
// 얕은 복사 => 메모리 값을 복사하여 할당함 즉, obj와 otherObj의 포인터는 같은 메모리를 가리킨다.
const obj = {
value : 1
}
const otherObj = obj
otherObj.value = 2
// 2
console.log(obj.value)
// true
console.log(obj === otherObj)
obj 객체를 다른 객체( otherObj )에 할당하였으며 이를 참조 할당이라고 부릅니다.
복사 후에 otherObj의 value값을 변경하였더니 기존의 obj.value의 값도 같이 변경된 것을 알 수 있습니다.
또한 두 객체를 비교( === ) 해도 true값이 나오게 됩니다.
얕은 복사 예제를 통해 JavaScript의 참조형은 얕은 복사가 된다고 볼 수 있으며
이것은 데이터가 그대로 생성되는 것이 아닌 해당 데이터의 참조 값( 메모리 주소 )을 전달하여 결국
메모리를 공유하는 것입니다.
깊은 복사 ( Deep Copy )
깊은 복사는 값 자체의 복사를 나타냅니다.
위 예제 원시 값의 복사가 깊은 복사입니다.
이것은 데이터가 복사될 때 독립적인 메모리에 값 자체를 할당하여 생성하는 것입니다.
하지만 객체의 깊은 복사는 어떻게 구현할까요??
객체의 깊은 복사
const obj = {
value : 1
}
const objerObj = Object.assign({}, obj)
// Object.assign({}, obj) 와 {...obj}는 같은 기능입니다.
const otherObj = {...obj}
otherObj.value = 2
// {value : 1}
console.log(obj)
// {value : 2}
console.log(otherObj)
// false
console.log(obj === otherObj)
Object.assign() 구문과 {... obj} 구문으로 메모리 주소를 다르게 할당하는 객체의 깊은 복사를 할 수 있습니다.
따라서 두 객체의 포인터는 서로 다른 메모리 주소를 가리킵니다.
하지만 위에서 사용한 두 가지 구문은 2차원 객체에 깊은 복사를 하지 않습니다.
2차원 배열 깊은 복사 예시 코드
const obj = {
depth : 1,
innerObj : {
depth : 2
}
}
const otherObj = {...obj}
otherObj.depth = 222
otherObj.innerObj.depth = 333
// {depth : 1, innerObj : { depth : 333} }
console.log(obj)
// {depth : 222, innerObj : {depth : 333} }
console.log(otherObj)
// false
console.log(obj === otherObj)
// true
console.log(obj.innerObj === otherObj.innerObj)
위의 예시 코드로 2차원 이상의 배열은 깊은 복사( 서로 다른 메모리 주소 값 참조 )가 이루어지지 않는 것을 알 수 있습니다.
즉, 2차원 객체에서의 깊은 복사는 재귀 호출 혹은 반복문을 이용해 깊은 복사를 할 수 있습니다.
재귀 함수 예시 코드입니다.
function deepCopy(obj){
if(obj === null || typeof obj !== "object"){
return obj;
}
let copy = {}
for(let key in obj){
copy[key] = deepCopy(obj[key])
}
return copy;
}
const obj = {
depth : 1,
innerObj : {
depth : 2,
}
}
const otherObj = deepCopy(obj)
otherObj.innerObj.depth = 333
// { depth : 1, innerObj : {depth : 2} }
console.log(obj)
// { depth : 1, innerObj : {depth : 333} }
console.log(otherObj)
// false
console.log(obj === otherObj)
// false
console.log(obj.innerObj === otherObj.innerObj)
deepCopy 함수의 매개변수로 obj 객체를 넣어 타입이 객체가 아닌 경우에는 바로 반환하며
객체인 경우 객체의 값만큼 반복하여 재귀를 호출하고 복사된 값을 반환합니다.
예시를 통해 복사된 otherObj 객체를 보면 2차원 객체 innerObj의 값도 깊은 복사가 이루어지는 것을 확인할 수 있습니다.
이번 포스팅을 통해 JavaScript의 얕은 복사와 깊은 복사에 대해 알아볼 수 있었습니다.
'Frontend > JavaScript' 카테고리의 다른 글
[JavaScript] 프로토타입에 대해 알아보자! ( prototype ) (0) | 2022.03.04 |
---|---|
[JavaScript] 호이스팅 ( hoisting ) 개념을 알아보자! (0) | 2022.01.25 |
[JavaScript] 업로드 이미지를 Base64로 변환하기 (0) | 2022.01.14 |