면접 연습을 하면서 '자바스크립트 데이터 타입에 대해 설명해주세요'에 대한 답변으로 메모리 관점에서 원시형과 참조형에 대해 설명했다. '원시형은 정말 값이 저장되나요?'라는 꼬리 질문이 들어왔고, 힙 영역에 대해 구체적으로 설명하지 못한 부분이 있어 정리하고자 한다.
자바스크립트 메모리에는 콜 스택과 메모리 힙 영역이 있다.
스택 영역은 컴파일 시 데이터의 크기를 알고 있는 정적 데이터(원시 타입)를 저장하는 영역이다. 이 콜 스택에 실행 컨텍스트가 쌓이며, 함수 실행이 완료되면 해당 실행 컨텍스트는 콜 스택에서 사라지게 된다.
힙 영역은 참조 타입 데이터가 저장되는 영역이다. 객체가 생성될 때 메모리 공간이 동적으로 할당된다.
let a = 'abc';
변수 영역과 데이터 영역으로 구분하여 생각하면,
1. 변수 영역 중 빈 공간(@1003)을 확보 후 식별자(a)를 저장한다.
2. 데이터 영역 중 빈 공간(@5004)에 문자열 'abc' 값을 저장한다.
3. 변수 영역에서 a 라는 식별자를 찾아, (2)의 주소를 @1003에 대입한다.
(파란색 변수a처럼 콜 스택에 저장된다는 점을 참고하기 위한 이미지)
이 식별자 a는 '실행 컨텍스트(Execution Context)의 렉시컬 환경(Lexical Environment)'이라는 곳에 저장되며 즉, 스택에서 일어나는 일이다.
이처럼 변수 영역에 데이터를 직접 대입하지 않고, 굳이 데이터 영역에 저장하고, 주소값으로 접근하는 단계를 거치는 이유는, '데이터 변환을 자유롭게' 할 수 있게 함과 동시에 '메모리를 효율적으로 관리'하기 위함이다.
변수와 상수를 구분(const, let, var) 짓는 영역은 위에서 언급한 변수 영역이며,
불변성의 여부를 구분(원시 타입, 참조 타입) 짓는 영역은 데이터 영역이다.
기본형 데이터(숫자, 문자열, boolean, null, undefined)는 모두 불변 값이다. 이는 메모리 데이터 영역에 저장된 값 자체는 변경되지 않는다는 의미이다. (새로운 공간에 값을 할당하고 변수 영역의 참조값을 변경하는 동작을 통해서 '변수의 값'을 변경하는 것이다) 즉, 한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 이상 영원히 변하지 않는다.
식별자 a의 데이터에 ‘abcdef’를 재할당하는 경우 기존에 ‘abc’가 저장되었던 공간에 ‘abcdef’를 할당하는 것이 아니라, ‘abcdef’라는 문자열을 새로 만들어 별도의 공간에 저장하고, 새로 저장된 공간의 주소를 변수 공간에 연결한다. 기존 문자열에 어떤 변환을 가하든(문자를 추가 및 제거) 상관 없이 무조건 ‘새로’ 만들어서 별도의 공간에 저장한다.
const obj = { a: 1, b: 'bbb' } ;
(위에 첨부한 동일한 이미지에서, 분홍색 변수b,c,d처럼 메모리 힙에 별도의 변수 영역을 확보한다는 것을 참고하자)
원시 타입과 차이점이 바로 이 객체의 변수 영역이 별도로 존재한다는 점이다.
객체가 별도로 할애한 영역은 변수 영역일 뿐, 데이터 영역은 기존의 메모리 공간(스택)을 그대로 활용하고 있다.
데이터 영역에 저장된 값은 모두 불변 값인데, 변수는 다른 값을 얼마든지 대입 할 수 있다는 특징을 가지고 있다.
그래서 흔히 참조형 데이터가 가변값이라고 하는 것이다.
예를 들어 위에서 선언했던 obj 프로퍼티 a에 대해 10으로 변경을 했다면,
여기서 주목할 점은 @1002에 있는 주소값은 바뀌지 않았다는 것이다. 즉, 새로운 객체가 만들어지지 않고, 객체 내부 값만 변경되었다.
따라서 obj2 = obj1
이 아니라 obj2 = { c: 20, d: 'ddd' }
처럼 객체 자체를 할당함으로써 값을 직접 변경할 때는
메모리의 데이터 영역의 새로운 공간(@5002)에 새 객체의 주소(@7106 ~ ?)가 저장되고, 그 주소(@5002)를 변수 obj2 위치(@1003)에 저장한다.
이처럼 참조형 데이터가 가변값이라고 설명할 때의 "가변"은 데이터 내부의 프로퍼티를 변경할 때만 성립하는 것이다.
참고