0. 자바스크립트의 데이터 유형
JS의 데이터 유형은 2 가지가 있다.
- primitive (=원시형, 기본형)
- number, string, boolean, null, undefined
- reference (=참조형, 객체형)
- object, array, function …
왜 이렇게 두 분류로 나눈걸까. 결론부터 말하자면 데이터가 저장되는 메모리 영역의 특징 때문이다. + (여러 속성으로 이루어진 하나의) 객체를 저장하기 위함…? 자바스크립트 메모리 영역은
- 변수 영역 : 변수의 식별자가 저장된다.
- 데이터 영역 : 변수가 저장된다.
으로 나누어진다.
데이터 메모리 영역은 한 번 할당되면 재할당이 불가능하다. GC가 수거해 가기전까지는… 반면 변수 영역은 한 번 할당한 영역에 재할당이 가능하다.
따라서 원시형과 참조형은 불변성과 가변성 측면에서 차이점이 있다.
사실, 내부적으로 보면 원시형과 참조형 변수에는 모두 어떤 주소값이 저장되어 있다. 그 주소값이 바로 데이터 영역이냐 변수 영역이냐가 원시형/참조형을 구분하게 된다.
그럼 자바스크립트가 원시형/참조형 데이터들을 어떻게 메모리에 할당하고 접근하는지 보자보자보자.
1. 자바스크립트의 데이터 할당
1
2
var name = 'jane'
name = name + 'doe'
name이라는 원시형 변수를 선언과 동시에 ‘jane’으로 할당하는 코드다. 이를 자바스크립트의 메모리 관점에서 본다면, 아래와 같다.
name이라는 변수는 ‘jane’이라는 문자열이 저장된 메모리 주소의 주소값을 가지고 있다. 그리고 name변수에 ‘doe’문자열을 이어붙여서 name에 새로 할당한다. 이를 메모리 관점에서 보면,
- 기존의 100 주소에 있던 데이터(‘jane’)가 새로운 이어붙인 데이터(‘jane doe’)로 바뀌는 것이 아니라,
- 새로운 데이터 영역을 할당받고 그 주소값으로 변경된다.
- 원시형 타입이 불변하다고 하는 이유가 여기 있다. 한 번 할당된 데이터 영역의 데이터는 GC가 수거해가지 않는 이상 바뀌지 않는다. 새로운 데이터가 생기고 할당될 뿐이다.
그렇다면 참조형 데이터의 데이터 할당은 어떨까.
1
2
3
4
var person = {
age: 25,
gender: 'female'
}
- 참조형은 각 객체마다 별도의 변수 영역을 갖는다는 것이 특징이다. 각 속성의 값은 같은 데이터 영역을 사용한다.
- person 변수에는 해당 객체의 속성들을 가리키고 있는 주소값(100)이 할당된다.
- 데이터 영역의 100번지 주소는 person 객체의 속성의 주소값을 데이터값으로 가지고 있다.
결과적으로 변수가 변수에 저장된 값에 도달하기까지 원시형 변수는 다리가 1 개, 참조형 변수는 다리가 2개 놓여져 있다고 비유할 수 있다.
- 원시형 변수는 변수에 저장된 주소 값으로 가면 바로 데이터가 있지만 ( 1단계 )
- 참조형 변수는 변수에 저장된 주소 값으로 가면 ( 1단계 ) 바로 객체의 속성들의 데이터 값이 있는 것이 아니라, 또 다른 주소값이 저장되어 있고, 그 주소값으로 가면 ( 2단계 ) 비로소 객체의 속성들의 값을 가리키는 주소값이 저장되어 있다.
중요한 것은..**원시형/참조형 변수의 불변성/가변성의 대상은 메모리 영역이다!! **
변수 영역의 불변/가변을 따지는 것은 참조값이 바뀌느냐 아니는냐로 볼 수 있는데, 이렇게 되면, 원시형은 데이터가 바뀔 때마다 새로운 영역의 주소값이 할당되기 때문에 변수 영역이 매 번 바뀐다는 점에서 가변이 되고,
참조형은 아무리 속성값이 바뀐다고 해도 변수 영역의 주소값 자체가 바뀌는 것이 아니기 때문에 불변이라고 볼 수 있다. 아예 새로운 객체를 만들고 할당해버리면 또 가변이라고 볼 수 있긴 하다.
2. 자바스크립트의 데이터의 복사
데이터를 할당 했으니 복사를 해보자보자.
1
2
3
4
5
var a = 100
var b = a
a = 123
console.log(b) // 100
원시형의 할당
- 변수 영역에 식별자 a를 할당한다.
- 데이터 영역에 100을 할당한다. (주소 : @101)
- 변수 a = @101
- 변수 영역에 식별자 b를 할당한다.
- 변수 b = @101 (a에 저장된 주소값)
원시형의 데이터 갱신
- 데이터 영역에 123을 새롭게 할당한다 (주소 : @202)
- 변수 a = @202
- 이 시점에서 a와 b는 서로 다른 주소값을 가리키게 된다. ( = 연결이 끊어짐)
-> 변경/재할당이 안 되는 데이터 영역의 특성상 데이터 갱신이 일어날 때마다 새로운 주소값이 변수 영역에 할당되게 되면서 결과적으로는 원시형의 복사는 값의 복사가 된다. ( = 둘 중 어느 하나가 변경되도 서로에게 영향을 미치지 않음.)
1
2
3
4
5
6
7
var obj = {
a : 100,
b : 200
}
var copy = obj
copy.a = 111
console.log(obj.a) // 111
참조형의 할당
- 변수 영역에 obj가 할당된다.
- 객체의 변수 영역에 a와 b가 할당되고,
- 데이터 영역에 100과 200이 할당되고 각 주소값이 객체의 변수 영역의 a와 b에 저장된다.
- obj는 객체의 변수 영역을 가리키는 주소값이 저장된 데이터 영역의 주소값이 저장된다. (말이 너무 어렵네…)
- obj에는 값이 아니라 참조값이 저장되어 있다.
참조형의 복사
- 변수 영역에 copy가 할당된다.
- obj에 저장된 주소값이 copy에 저장된다.
- 객체의 a 속상값이 바뀐다. 새로운 데이터 영역이 할당되고 (111)
- 객체의 변수 영역의 a 값이 새로운 데이터 영역의 주소값으로 바뀐다.
- 내부의 값이 바뀌었을 뿐, a와 b의 저장된 주소값이 변경된 것은 아니다 ( 원시형과 다른 부분)
- 두 식별자가 모두 같은 주소값을 바라보고 있다는 것은 둘 중 어느 하나가 변경되도 모두 에게 영향을 미친다는 의미로 볼 수 있다.
원시형/참조형의 복사 결과가 달라지는 이유는 역시 데이터 할당 때문이다.
3. 정리
- 원시형/참조형 식별자 모두 어떤 주소값이 저장되어 있다.
- 그 주소값이 데이터 영역의 주소를 가리키면, 매번 새로운 주소값으로 바뀌게 된다. ( = 데이터 영역은 불변하므로 갱신때마다 새로운 데이터 영역이 할당되고 새로운 주소값이 생기게 됨) -> 원시형
- 그 주소값이 변수 영역의 주소를 가리키면, 내부 속성들의 주속값만 바뀔 뿐, 식별자에 저장된 주소값이 바뀌는 것은 아니다. -> 참조형
4. 느낀점
primitive, reference 두 단어의 의미를 생각해보니 이보다 더 적합한 단어가 있을까 싶었다. 원시형, 즉 원자성을 띄기 때문에 원본 데이터에 바로 참조하는 데이터. 참조형, 원본 데이터에 바로 접근하는 것이 아니라 중간의 무언가를 통해 한 단계 거쳐서 참조하는 데이터.
메모리 관점에서 코드를 바라보면 평소에 당연하게 생각했던 것들도 갑자기 낯설어진다.