JS에서 변수의 데이터 타입은 크게 2가지로 나누어지는데 원시타입(Primitive type)과 객체/참조 타입(Object/Reference type)이다.
원시 타입은 변경 불가능한 값(immutable value)이며 pass-by-value(값에 의한 전달)의 형태로 전달된다.
객체/참조 타입은 변경이 가능한 값을 가지며 (배열의 인덱스 추가, 객체 프로퍼티 추가 등) pass-by-reference(참조에 의한 전달) 방식으로 전달된다.
원시 타입의 데이터는 변경이 불가능하기 때문에,
메모리를 할당할 때, 해당 데이터에 딱 맞는 메모리 공간을 할당한다.let a = 10;
식별자 메모리 주소 데이터 값 a 0001 0111 10 Memory Allocation & Data 편에서 보았듯이 프로그램은 OS(프로세스)에 어느정도 메모리 공간이 필요한지 요청하고, 프로세스는 메모리 영역의 상황을 보고 요청한 만큼의 메모리 공간을 할당한다.
💡 만약 변수의 값을 재선언 하면 메모리 공간에선 어떤일이 일어날까?
let a = 10; a = 'hello';
JS는 Programming Language 편에서 설명했듯이 동적 타입(Dynamic Type)의 언어다.
(즉 변수의 타입이 자동으로 결정된다.)
원시 타입 변수는 재선언 하는 순간, 기존 메모리 공간의 값이 변경되는게 아니라, 변경된 데이터를 담은 완전히 새로운 메모리 공간을 생성한다.
💡 그러면 기존 메모리 공간은 어떻게될까?
JS 엔진은 가비지 컬렉터 라는 자동 메모리 최적화 프로그램이 탑재되어있다.
만약 더 이상 사용되지 않는 메모리 영역이 있다고 판단되면, 해당 메모리 영역을 할당 해제한다.
💡변수 a
와b
가 같은 값을 가진다면?
let a = 10; a = 'hello'; let b = 'hello';
a
와b
가 같은 값을 선언했다면,a
와b
의 포인터가 같은 메모리 주소를 가르킨다..
(같은 값을 가진 새로운 메모리를 할당하지 않는다.🙅->
이는 메모리 낭비이기 때문이다.)
💡변수 b = a; a = a+1; 과 같은 상황일땐 어떨까?
1. `a`와 `b`는 처음에 같은 메모리 주소를 가르킨다.a = '10'; let b = a; a = a+1;
a=a+1
에 의해a
는 새로운 메모리 주소를 가르킨다.b
는 재할당된a
의 주소를 따라가진 않는다.
왜냐하면 원시 타입 데이터는 기본적으로 불변(immutable value)이기 때문이다.
객체/참조 타입의 데이터는 변경이 가능하기때문에,
원시 타입 데이터와는 다른 유연한 메모리 공간이 필요하다.let a = { Type : White, Country : France, Region : Burgundy,}
위에 보다시피 객체인 변수
a
의 메모리 주소는 다른 메모리 영역의 주소를 가르킨다는 것을 알 수 있다.
왜 위와 같은 현상이 일어날까?
💡여기서부터는 개인적인 생각이다.
불변적인 원시 타입 데이터와는 다르게, 객체/참조 타입 데이터는 변화할 수 있다. 가령
let b = 10;
const a = document.getElementsByTagName('input')[0]
일때 프로그램은 b
의 데이터 크기를 알고 해당 데이터 크기 만큼의 메모리를 할당할 수 있지만,
a
와 같이 코드가 실행하기 전에 값을 알 수 없는 경우, 해당 변수에 얼마만큼의 값이 채워질지 예측할 수 없다.
때문에 해당 변수의 메모리 영역을 임의로 설정해야 할 수 밖에 없을것이다.
위의 내용을 정리해보면,
프로그램은, 데이터의 크기를 알 수 있는 영역과 알 수없는 영역으로 나누어진다.
만약 우리가 정확히 크기를 알고있는 데이터들은 딱 맞는 크기의 메모리 영역에 보관하는것이 당연히 좋을 것이다.
💡공간이 낭비되어봤자 좋을것이 없기 때문
하지만 우리가 정확한 크기를 모르는 데이터는, 메모리 영역을 조금 여유있게 할당하는 것이 좋을 것이다.
💡우리가 단체로 무언가 먹을걸 주문할때, 차라리 남는게 좋지 부족하면 안되지 않는가?
또한 그 와중에 메모리 최적화를 위해 해당 메모리 영역의 사이즈를 적당히 정하고,
사용자에게 명시해도 좋을 것이다.💡가령 트위터의 글자수 제한 처럼 말이다.
그렇다면, 해당 2가지 영역을 나누어 따로 메모리를 관리한다면 더 좋지 않을까?
💡 우리가 호텔을 갈때, 객실의 수와 객실 별 투숙 가능한 인원은 정확히 정해져있지만,
투숙객 중 얼마나 많은 인원이 이용할지 모르는
헬스장이나 수영장, 뷔페, 식장 등의 사이즈는 호텔 규모에 맞게 적당히 구성되어 있는것 처럼 말이다.
💡 이처럼 원시 타입 데이터 및 코드 공간의 데이터를 호텔 객실에,
크기를 알 수 없는 동적인 데이터들은
들어오는 크기에 맞게 컨벤션 홀이나 수영장, 카페 등에 보관한다면,
좀 더 효율적이게 메모리를 관리할 수 있지 않을까? 라고 생각을 해본다.
💡변수 a= 10; / a= [0, 1, 2]
a = 10; a = [0, 1, 2];
a
는 원시타입 데이터에서 참조 타입 데이터로 변환이 가능하다.
💡a=[0, 1, 2] / b = a / a.push(3); 과 같은 상황일땐 어떨까?
const a = [0, 1, 2]; let b = a; a.push(3);
원시타입과는 다르게 객체/참조 타입 데이터에서는 데이터 값이 변경되더라도, 메모리주소가 변경되진 않는다. 메모리 주소가 변경되지 않았음으로
b
또한let b = a
에서 선언한a
와 같은 메모리 주소를 그대로 가르킨다.
위의 예시와 같은 이유로 객체, 배열등의 변수는
const
로 선언한다.
변수는 되도록 const로 선언되는게 좋다.
변할 수 없기 때문에,
예측불가능한 움직임을 만들어내지 않기 때문이다.
우리에게 반가운 소식은, 객체/참조타입의 데이터는 값이 변경되는게 아닌, 수정되는 것이기 때문에 const를 사용해도 값이 변경된 것처럼 사용할 수 있다.
const a = []
for(i=0; i<4; i++){
a.push(i);
}
a // [0, 1, 2, 3]
위의 a는 값이 수정된 것이지 a라는 변수의 새로운 메모리 공간이 할당된 것이 아니다.
💡 JS에서 데이터 타입은 크게 2가지로 나누어져있다.
💡 변수를 선언한다는 것은, 변수에 값을 부여하는게 아니라, 값이 담긴 메모리의 메모리 주소를 부여하는 것이다.
💡 원시 타입 데이터는 각각의 고유한 메모리 영역을 가진다.
💡 객체/참조 타입 데이터들 만을 위한 공간이 따로 있을 것으로 추측된다.
💡 객체/참조 타입 데이터를 변수로 선언하면, 해당 변수의 메모리 주소는 다른 메모리 영역의 주소를 참조하는 포인터를 값으로 가진다.
💡 객체/참조 타입 데이터로 선언한 변수 내부 값이 변경되어도, 해당 값을 가르키는 메모리 주소 포인터는 변하지 않는다 (const로 선언해도 값을 수정할 수 있다.)