Home 코어 자바스크립트 | 1. 데이터의 가변성과 불변성
Post
Cancel

코어 자바스크립트 | 1. 데이터의 가변성과 불변성

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’으로 할당하는 코드다. 이를 자바스크립트의 메모리 관점에서 본다면, 아래와 같다.

jsmemory-primitive

name이라는 변수는 ‘jane’이라는 문자열이 저장된 메모리 주소의 주소값을 가지고 있다. 그리고 name변수에 ‘doe’문자열을 이어붙여서 name에 새로 할당한다. 이를 메모리 관점에서 보면,

jsmemoryprimitive2

  • 기존의 100 주소에 있던 데이터(‘jane’)가 새로운 이어붙인 데이터(‘jane doe’)로 바뀌는 것이 아니라,
  • 새로운 데이터 영역을 할당받고 그 주소값으로 변경된다.
  • 원시형 타입이 불변하다고 하는 이유가 여기 있다. 한 번 할당된 데이터 영역의 데이터는 GC가 수거해가지 않는 이상 바뀌지 않는다. 새로운 데이터가 생기고 할당될 뿐이다.

그렇다면 참조형 데이터의 데이터 할당은 어떨까.

1
2
3
4
var person = {
    age: 25, 
    gender: 'female'
}

jsmemory-reference

  • 참조형은 각 객체마다 별도의 변수 영역을 갖는다는 것이 특징이다. 각 속성의 값은 같은 데이터 영역을 사용한다.
  • 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 두 단어의 의미를 생각해보니 이보다 더 적합한 단어가 있을까 싶었다. 원시형, 즉 원자성을 띄기 때문에 원본 데이터에 바로 참조하는 데이터. 참조형, 원본 데이터에 바로 접근하는 것이 아니라 중간의 무언가를 통해 한 단계 거쳐서 참조하는 데이터.

메모리 관점에서 코드를 바라보면 평소에 당연하게 생각했던 것들도 갑자기 낯설어진다.

This post is licensed under CC BY 4.0 by the author.

Dockerfile | RUN, CMD, ENTRYPOINT 명령어 차이점

JS에서 안전하게 널값 처리하기 Optional chaining(?.) & Nullish coalescing operator (??)