서론
이 글은 ‘코어 자바스크립트(정재남 저)’라는 책의 내용을 바탕으로 합니다. 책에는 더 자세한 예제와 설명이 되어 있으니 책을 꼭 읽어보시길 바랍니다.
자바스크립트의 데이터 타입
JS의 데이터타입은 기본형(primitives values), 참조형(Object types) 데이터로 나뉩니다. 기본형의 경우 숫자, 문자열, 불리언, null, undefined 등을 포함합니다. 참조형 데이터는 객체, 배열, 함수 등을 포함합니다.
기본형, 참조형 데이터의 차이는 할당이나 연산 시 기본형은 값이 담긴 주솟값을 바로 복제하는 반면, 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다는 것입니다.
기본형 데이터의 할당, 연산 처리 과정
var str = ‘abc’;
- 비어 있는 변수 공간을 확보하고 식별자(변수명)를 str으로 지정
- 메모리의 빈 공간에 문자열 ‘abc’를 저장
- 변수 중 str라는 식별자를 검색
- ‘abc’ 문자열의 주소를 str의 메모리 주소 공간에 대입 (= 변수 str이 ‘abc’의 메모리 주소를 가리키게 함)
Q. 왜 값을 변수 영역에 바로 대입하지 않나요? A. 만약 값을 변수에 바로 대입할 경우 ‘abc’를 저장하는 변수가 5000개 있다면 ‘abc’를 값으로 하는 변수 5000개를 만들고, 5000개의 메모리를 확보해야합니다. 그러나 ‘abc’의 메모리 주소를 변수에 대입하는 방식을 사용하면 메모리에 한번만 ‘abc’를 넣기 때문에 메모리를 절약할 수 있습니다.
문자열 데이터를 변환할 경우 (& 가비지 컬렉터 & 불변값)
var str = 'abc';
str = str + 'def';
Q. 그럼 문자열 ‘abc’에 ‘def’를 붙이려는 경우엔 메모리 내 ‘abc’가 ‘abcdef’로 변하나요?
A. 이런 경우에는 ‘abcdef’라는 문자열을 새로 만들어 별도의 메모리 공간에 저장하고, 그 주소를 str에 새로 연결하게 됩니다. 그 후 ‘abc’ 데이터는 자기 주소를 저장한 변수가 하나도 없어 가비지 컬렉터의 수거 대상이 됩니다.
=> 그렇기 때문에 기본형 데이터는 **불변값(Immutable)**이라는 특성을 갖습니다. 메모리 공간 내에서 **한번 만들어진 데이터(!=변수의 값)**는 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않습니다.
참조형 데이터의 할당
지금까지 기본형 데이터를 보았다면 객체로 이루어진 참조형 데이터들은 어떻게 할당될까요? 책 ‘코어 자바스크립트’에서는 임의의 변수 영역/ 데이터 영역(메모리)을 구분해 이해를 돕습니다.
var obj = {
a:1,
b:'bbb'
};
- obj라는 변수가 존재하지 않으니 변수 영역의 빈 공간을 찾아 그 공간의 식별자를 obj로 지정
- 메모리에 obj의 프로퍼티(a,b)를 넣어야하는데 obj객체가 여러 프로퍼티를 가지고 있으니 별도의 변수 영역을 마련하고 그 영역의 주소를 obj의 값으로 지정
- 별도의 변수 영역에 각각의 프로퍼티 값을 식별자로 지정하고 그 값을 데이터 영역에 지정 => obj가 a,b를 담는 변수 영역을 가리키고, a,b를 담은 변수 영역은 각각 1, ‘bbb’라는 값을 가지는 데이터 영역을 가리키게 됨.
객체를 복사한 뒤 값을 바꾼다면?
var obj1 = {c:10, d: 'ddd'};
var obj2 = obj1;
obj2.c = 20;
console.log(obj1, obj2);
이 코드를 실행하면 obj2의 프로퍼티의 값을 바꿨는데 obj1의 프로퍼티의 값까지 바뀌어버립니다. 왜일까요?
그건 바로 저 코드가 obj2가 참조하는 데이터의 값을 바꾸는 것이기 때문입니다. obj1를 복사해 obj2 객체를 만드는 것은 사실상 obj1가 참조하고 있는 주소값을 똑같이 참조하는 객체를 만드는 행위입니다. 그래서 데이터 변경 후에 obj2와 같은 데이터를 참조하고 있던 obj1의 값까지 달라져 버린 것입니다. => 그렇기 때문에 우리는 변하지 않는, ‘불변 객체‘가 필요합니다.
얕은 복사(shallow copy), 깊은 복사(deep copy)
function copyObject(target) {
var result = {};
for(var prop in target) {
result[prop] = target[prop]
}
return result;
}
얕은 복사(shallow copy)란 for loop를 돌려 바로 아래 단계의 값만 복사하는 방법입니다. 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주소값만 복사됩니다. 즉, 얕은 복사를 할 경우 prop(프로퍼티)가 참조형 데이터일 경우 기존 데이터를 그대로 참조하게 됩니다. 이를 방지하기 위해선 **참조형 데이터가 있을 때마다 얕은 복사를 재귀적으로 수행하는 깊은 복사(deep copy)**가 수행되어야 합니다.
var copyObjectDeep = function(target) { // 깊은 복사
var result = {};
if (typeof target === 'object' && target !== null) { // target이 객체인 경우
for (var prop in target) {
result[prop] = copyObjectDeep(target[prop]); //재귀적으로 카피
}
} else {
result = target;
}
return result;
}
}