1-4 기본형 데이터와 참조형 데이터

박재현·2022년 5월 20일
0
post-thumbnail

불변값

변수와 상수를 구분하는 성질은 "변경 가능성"이다.

변수와 상수를 구분 짓는 변경 가능성의 대상은 변수 영역 메모리이다.
불변성 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리이다.

기본형 데이터인 Number, String, Boolean, null, undefined, Symbol은 모두 불변값이다.
숫자와 문자열을 예로 들어 불변성의 개념을 알아보자.

불변성

let a = 'abc';
a = a + 'def';

변수 a에 문자열 'abc'를 할당했다가 새로운 문자열 'abcdef'를 만들어 그 주소를 변수 a에 저장한다. 'abc'와 'abcdef'는 완전히 별개의 데이터이다.

let b = 5;
let c = 5;

변수 b에 숫자 5를 할당한다. 컴퓨터는 데이터 영역에서 5를 찾고, 없으면 데이터 공간을 하나 만들어 저장한다. 그 주소를 b에 저장한다.
그 다음 줄에서는 컴퓨터가 데이터 영역에서 5를 찾는다. 아까 만든 그 주소를 재활용한다.

b = 7;

변수 b의 값을 7로 바꾸고자 한다. 기존에 저장했던 7을 찾아서 있으면 재활용하고, 없으면 새로 만들어서 b에 저장한다. 5 와 7 모두 다른 값으로 변경할 수 없다.

변경은 새로 만드는 동작을 통해서만 이루어진다. 이것이 불변값의 성질이다.

가변값

참조형 데이터의 기본적인 성질은 가변값인 경우가 많지만 설정에 따라 변경 불가능한 경우도 있고 아예 불변값으로 활용하는 방안도 있다.

참조형 데이터를 변수에 할당하는 과정

let obj1 = {
  a: 1,
  b: 'bbb'
};


1. 컴퓨터는 우선 변수 영역의 빈 공간(@1002)을 확보하고, 그 주소의 이름을 obj1으로 지정한다.
2. 임의의 데이터 저장공간(@5001)에 데이터를 저장하려고 보니 여러 개의 프로퍼티로 이뤄진 데이터 그룹이다. 이 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고, 그 영역의 주소(@7103~?)를 @5001에 저장한다.
3. @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정한다.
4. 데이터 영역에서 숫자 1을 검색한다. 검색 결과가 없으므로 임의로 @5003에 저장하고, 이 주소를 @7103에 저장한다. 문자열 'bbb'역시 임의로 @5004에 저장하고, 이 주소를 @7104에 저장한다.

객체의 변수(프로퍼티)영역이 별도로 존재한다. 객체가 별도로 할애한 영역은 변수 영역일 뿐 '데이터 영역'은 기존의 메모리 공간을 그대로 활용하고 있다. 데이터 영역에 저장된 값은 모두 불변값이다. 그러나 변수에는 다른 값을 얼마든지 대입할 수 있다.
=> 그러므로, 참조형 데이터는 불변하지 않다.

참조형 데이터의 프로퍼티 재할당

let obj1 = {
  a: 1,
  b: 'bbb'
}
obj1.a = 2;

obj1의 a프로퍼티에 숫자 2를 할당하려고 한다.

  1. 데이터 영역에서 숫자 2를 검색한다.
  2. 검색 결과가 없으므로 빈 공간인 @5005에 저장하고, 이 주소를 @7103에 저장한다.

변수 obj1이 바라보고 있는 주소는 @5001로 변하지 않았다.

중첩된 참조형 데이터(객체)의 프로퍼티 할당

let obj = {
  x: 3,
  arr: [3, 4, 5]
};


1. 컴퓨터는 우전 변수 영역의 빈 공간(@1002)을 확보하고, 그 주소의 이름을 obj로 지정한다.
2. 임의의 데이터 저장공간(@5001)에 데이터를 저장하려는데, 이 데이터는 여러 개의 변수와 값들을 모아놓은 그룹(객체)이다. 이 그룹의 각 변수(프로퍼티)들을 저장하기 위해 별도의 변수 영역을 마련하고(@7103~?), 그 영역의 주소를 @5001에 저장한다.
3. @7103에 이름 x를, @7104에 이름 arr를 지정한다.
4. 데이터 영역에서 숫자 3을 검색한다. 없으므로 임의로 @5002에 저장하고, 이 주소를 @7103에 저장한다.
5. @7104에 저장할 값은 배열로서 역시 데이터 그룹이다. 이 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고(@8104~?), 그 영역의 주소 정보(@8104~?)를 @5003에 저장한 다음, @5003을 @7104에 저장한다.
6. 배열의 요소가 총 3개이므로 3개의 변수 공간을 확보하고 각각 인덱스를 부여한다(0, 1, 2).
7.데이터 영역에서 숫자 3을 검색해서(@5002) 그 주소를 @8104에 저장합니다.
8. 데이터 영역에서 숫자 4가 없으므로 @5004에 저장하고, 이 주소를 @8105에 저장한다.
9. 데이터 영역에 숫자 5가 없으므로 @5005에 저장하고, 이 주소를 @8105에 저장한다.

obj.arr[1]을 검색하고자 하면 메모리에서는 다음과 같은 검색 과정을 거친다.

  1. obj 검색 1: obj라는 식별자를 가진 주소를 찾는다(@1002).
  2. obj 검색 2: 값이 주소이므로 그 주소로 이동한다(@5001).
  3. obj 검색 3: 값이 주소이므로 그 주소로 이동한다(@7103 ~ ?).
  4. obj.arr 검색 1: arr이라는 식별지를 가진 주소를 찾는다(@7104).
  5. obj.arr 검색 2: 값이 주소이므로 그 주소로 이동한다(@5003).
  6. obj.arr 검색 3: 값이 주소이므로 그 주소로 이동합니다(@8104 ~ ?)
  7. obj.arr[1] 검색 1: 인덱스 1에 해당하는 주소를 찾는다(@8105).
  8. obj.arr[1] 검색 2: 값이 주소이므로 그 주소로 이동한다(@5004).
  9. obj.arr[1] 검색 3: 값이 숫자형 데이터이므로 4를 반환한다.

@1002 -> @5001 -> (@7103 ~ ?) -> @5003 -> (@8104 ~ ?) -> @8105 -> @5004 -> 4 반환

재할당 명령을 내린다면?

obj.arr = 'str';

@5006에 문자열 'str'을 저장하고 그 주소를 @7104에 저장한다.

@5003의 참조 카운트가 0이 되고, 연쇄적으로 @8104 ~ ?의 각 데이터들의 참조 카운트도 0이 된다.
참조 카운트가 0인 메모리주소는 가비지컬렉터의 수거대상이 되고 런타임 환경에 따라 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거된다.

변수 복사 비교

기본형 데이터와 참조형 데이터의 차이

기본형 데이터를 보자.

let a = 10;
let b = a;
  1. 변수 영역의 빈 공간 @1001을 확보하고 식별자를 a로 지정한다.
  2. 숫자 10을 데이터 영역에서 검색하고 없으므로 빈 공간 @5001에 저장한 다음, 이 주소를 @1001에 넣는다.
  3. 변수 영역의 빈 공간 @1002를 확보하고 식별자를 b로 지정한다.
  4. 식별자 a를 검색해 @1001에 저장된 값인 @5001을 들고 좀 전에 확보해둔 @1002에 값으로 대입한다.

참조형 데이터를 보자.

let obj1 = { c: 10, d: 'ddd' };
let obj2 = obj1;
  1. 변수 영역의 빈 공간 @1003을 확보하고 식별자를 obj1으로 지정한다.
  2. 데이터 영역의 빈 공간 @5002를 확보하고 데이터 그룹이 담길 (@7103 ~ ?)을 확보해 그 주소를 저장한다.
  3. @7103에는 식별자 c를, @7104에는 식별자 d를 입력한 다음, c에 대입할 값 10을 데이터 영역에서 검색한다.
  4. @5001에 이미 저장돼 있으므로 이 주소를 @7103에 저장한다.
  5. 문자열 'ddd'를 데이터 영역에서 검색하고 없으므로 빈 공간 @5003에 저장한 다음, 이 주소를 @7104에 넣는다.
  6. 변수 영역의 빈 공간 @1004를 확보하고 식별자를 0bj2로 지정한다.
  7. 식별자 obj1을 검색해 그 값인 @5002를 들고, @1004에 값으로 대입한다.

변수 복사 이후 값 변경 결과 비교

let a = 10;
let b = a;
let obj1 = { c: 10, d: 'ddd' };
let obj2 = obj1;

b = 15;
obj2.c = 20;
  1. 숫자 15를 데이터 영역에서 검색하고 없으므로 빈 공간 @5004에 저장한 다음, 식별자 b의 값에 그 주소를 저장한다.
  2. 숫자 20을 데이터 영역에서 검색하고 없으므로 빈 공간 @5005에 저장한다.
  3. obj2를 변수 영역에서 검색하고, obj2의 값인 @5002가 가리키는 변수 영역에서 다시 c를 찾아 그곳에 @5005를 대입한다.

변수 a, b는 서로 다른 주소를 바라보게 됐으나, 변수 obj1과 obj2는 여전히 같은 객체를 바라보고 있는 상태이다.

일반적으로는 기본형도 결국 주솟값을 참조한다. 다만 기본형은 주솟값을 복사하는 과정이 한 번만 이뤄지고, 참조형은 한 단계를 더 거치게 된다.

객체 자체를 변경했을 때

obj2 = { c: 20, d: 'ddd' };

obj2에도 새로운 객체를 할당함으로써 값을 직접 변경한 경우, 데이터 영역의 새로운 공간에 개 객체가 저장되고 그 주소를 변수 영역의 obj2위치에 저장한다.

즉, 참조형 데이터가 "가변값"이라고 설명할 때의 "가변"은 참조형 데이터 자체를 변경할 경우가 아닌, 그 내부의 프로퍼티를 변경할 때만 성립한다.

profile
공동의 성장을 추구하는 개발자

0개의 댓글