얕은 복사와 깊은 복사, 자바스크립트
얕은 복사와 깊은 복사.. Shallow Copy vs Deep Copy
정말 헷갈리는 부분이 많았다. 정말로.
이제는 그만 헷갈리고자, 총 정리를 해보려한다.
0. 알아야 할 사전 지식
얕은 복사와 깊은 복사를 알기 전에 자바스크립트의 데이터 타입과 메모리 동작을 이해하는 것이 우선이다.
데이터 타입은 크게 원시형 / 참조형으로 구분되어있고 어떤 데이터를 변수에 저장하느냐에 따라 동작이 달라진다.
원시형을 먼저 살펴보자.
// 변수 a에 숫자 1을 할당하는 예시
var a = 1;
1. &27의 값에 undefined를 할당한다.
2. 식별자 a에 &27을 할당한다.
(& 기호는 메모리 주소를 의미한다. &27은 메모리의 27번 주소)
3. 새로운 공간인 &37에 1을 저장한다.
4. a에 &37을 할당한다.
원시형 데이터는 이런 방식으로 변수를 선언하고 할당한다.
참조형은 어떻게 다를까?
// 변수 obj에 객체를 할당하는 예시
var obj = { name: 'jin' }
1. &27의 값에 undefined를 저장한다.
2. 식별자 obj에 &27을 할당한다.
(& 기호는 메모리 주소를 의미한다. &27은 메모리의 27번 주소)
3. 새로운 공간인 &37에 객체를 저장할 공간의 주소인 &12를 할당한다.
즉, &37번 주소의 값이 &12이다.
원시형 데이터는 &37번 주소의 값이 1이었다.
4. &12(name)에 'jin'이라는 문자열 값을 저장한다.
5. obj에 &37을 할당한다.
결과
: obj 변수의 값은 &37이며, &37에는 객체 프로퍼티를 저장하고 있는 저장 공간의 주소를 값(&12)으로 가진다.
즉, 객체는 주소값을 이용해 참조한다.
1. 복사의 종류
복사의 종류에는 세 가지가 있다.
참조 복사(reference copy), 객체 복사(object copy), 깊은 복사(deep copy)
이번에도 코드를 통해 세 가지를 알아보자.
// 중첩된 객체 예시
var nestedObject = { name : 'jin', school : { middle : 'haemi', high : 'seoil' }}
// 참조 복사
var referenceCopyObject = nestedObject
// 객체 복사
var shallowCopyObject = shallowCopy(nestedObject)
function shallowCopy(targetObject) {
var returnObj = {}
// targetObject를 순회하면서 prop-targetObject[prop](key-value)쌍으로 returnObj에 추가
for (var prop in targetObject} {
returnObj[prop] = targetObject[prop]
}
// 새로 만들어진 객체 반환
return returnObj
}
// 깊은 복사
var deepCopyObject = deepCopy(nestedObject)
function deepCopy(target) {
var returnValue = {}
// null의 타입이 object인 버그가 있습니다..
if (typeof targetObj === 'object' && target !== null) {
// 만약 타겟이 객체라면 프로퍼티를 재귀 형식으로 호출
for (var prop in targetObject} {
returnValue[prop] = deepCopy(target[prop])
}
} else {
// 타겟이 객체가 아니라면 값 반환
returnValue = target
}
return returnObj
}
가정
nestedObject(&20)의 값이 &37이고, &37이 &26(name), &11(school)의 값을 가지고 있다.
&11은 객체를 값으로 가지므로, &11은 &98(middle), &67(high)를 값으로 가진다.
&98은 'haemi'라는 문자열을 값으로 가지고, &67은 'seoil'를 가진다.
복사 결과
referenceCopy의 값
: &37, nestedObject와 똑같은 객체를 가리키는 주소를 값으로 가진다.
shallowCopy의 값
: 새로운 객체를 할당했으므로 &20의 값과 &37의 값, &26의 값이 새로운 주소로 할당된다.
단, &11은 그대로 가지고 있다. 변수에 객체를 새로 할당할 때를 생각하면 이해할 수 있다.
변수 deepCopy의 값
: &37, &26(name), &11(school), &98(middle), &67(high)의 값, 즉 모든 주소의 값이 달라진다.
주소를 기준으로 살펴보자
원본 객체 : &20(nestedObject) -> &37 -> &26(name), &11(school) / &11(school) -> &98(middle), &67(high)
참조 복사 : &17(referenceCopyObject) -> &37 -> &26(name), &11(school) / &11(school) -> &98(middle), &67(high)
객체 복사 : &102(shallowCopyObject) -> &42 -> &21(name), &11(school) / &11(school) -> &98(middle), &67(high)
깊은 복사 : &134(deepCopyObject) -> &9 -> &244(name), &342(school) / &342(school) -> &213(middle), &1932(high)
2. 그래서 하고 싶은 말이 뭔데?
간혹, 학습 자료들을 보면 얕은 복사를 설명할때 참조 복사만, 객체 복사만 얕은 복사라고 이야기하는 경우가 있다.
이 경우에서 나는 헷갈렸기 때문에 얕은 복사와 깊은 복사를 나누는 기준을 공유하고자 한다.
얕은 복사와 깊은 복사의 영문 뜻은 다음과 같다.
shallow copy : A deep copy means that all of the values of the new variable are copied and disconnected from the original variable
deep copy : A shallow copy means that certain (sub-)values are still connected to the original variable.
즉, 복사한 객체가 원래 객체에 연결이 되어있으면 얕은 복사이고 연결이 되어있지 않으면 깊은 복사인 것이다.
이런 측면에서 얕은 복사는 참조 복사와 객체 복사로 이루어졌다고 볼 수 있다.
이제 더 이상 얕은 복사(참조/객체)와 깊은 복사를 헷갈리지 말자.
참고자료
https://www.youtube.com/watch?v=QFIfI8MIURQ