자바스크립트의 데이터 타입은 크게 2가지로 나뉘어져 있어 하나는 기본형(Primitive type),
다른 하나는 참조형(Reference type)이다.
이 둘을 어떤 기준으로 구분할 수 있을까?
엄밀히 말해서 둘 다 모두 복제가 가능하며, 기본형(Primitive type)은 할당이나 연산시 복제가 되는데 값이 담긴 주솟값을 바로 복제하고 참조형(Reference type)은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다는 점이 차이점이라고 말할 수 있다.
컴퓨터는 모든 데이터를 0과 1로 바꿔 기억하는데, 0 또는 1만 표현할 수 있는 하나의 메모리 조각을
비트(bit) 라고 하며, 메모리는 매우 많은 비트들로 구성되어 있고 각 비트는 고유한 식별자(Unique indentifier)를 통해 위치를 확인할 수 있다.
여기서 매우 많은 비트를 한 단위로 묶으면 검색 시간은 줄일 수 있으며, 표현할 수 있는 데이터의 개수가 늘어날 수 있지만 동시에 낭비되는 비트도 생긴다.
이러한 고민의 결과로 바이트(byte)라는 단위가 생기게 되는데, 1바이트는 8개의 비트로 구성되어
1비트 마다 0 또는 1의 두 가지 값을 표현할 수 있으므로 결국, 1바이트는 총 256(2^8)개의 값을 표현한다.
C/C++, 자바 등의 정적 타입 언어는 메모리의 낭비를 최소화하기 위해 데이터 타입별로 할당할 메모리 영역을 정해놓아야 하는데, 일정 이상의 큰 수를 입력하게 되면 정수형 타입(int)으로 형변환을 해야하는 번거로움이 있다.
하지만, 메모리 용량이 과거보다 월등히 커진 상황에서 등장한 자바스크립트는 상대적으로 메모리 관리에 대한 압박에서 자유로워졌기에 개발자가 앞서 말한 형변환을 걱정해야 하는 상황이 덜 발생하게 되었다.
모든 데이터는 바이트 단위의 식별자, 더 정확하게는 메모리 주솟값(Memory address)을 통해 서로 구분하고 연결할 수 있다.
식별자 - 어떤 데이터를 식별하는 데 사용하는 이름, 즉 변수명이다.
변수 - "변할 수 있는 수" 또는 "변할 수 있는 무언가"
여기서 무언가는 데이터이며 데이터는 숫자, 문자열, 객체, 배열 등이 있다.
var a;
변수란 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇이라고 정의할 수 있다.
위의 예시는 "변할 수 있는 데이터를 만들며, 이 데이터의 식별자는 a로 한다."로 설명할 수 있다.
<변수 선언에 대한 메모리 영역의 변화>
컴퓨터는 메모리에서 비어있는 공간 하나를 확보한다. (위의 그림에서는 임의로 1003번을 정함)
이 공간의 이름(식별자)을 a라고 지정하며, 여기까지가 변수 선언 과정이다.
var a; // 변수 a 선언
a = 'abc'; // 변수 a에 데이터 할당
var a = 'abc' // 변수 선언과 할당을 한 문장으로 표현
<데이터 할당에 대한 메모리 영역의 변화>
다시 말해서, 변수 영역에 저장된 주소 값에는 데이터를 새로 할당하지 않는다는 점이다.
var a = 'abc'
a = 'abcdef';
<문자열 반환에 대한 메모리 영역의 변화>
변수와 상수를 구분하는 성질 = "변경 가능성" (바꿀 수 있으면 변수, 없으면 상수)
변수와 상수를 구분 짓는 변경 가능성의 대상은 변수 영역 메모리이고, 불변성 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리이다.
<불변성 예시>
var a = 'abc';
a = a + 'def';
var b = 5;
var c = 5;
b = 7;
문자열 값과 숫자 모두 다른 값으로 변경할 수 없으며, 여기서 변경은 새로 만드는 동작을 통해서만 이루어진다.
참조형 데이터에 대해서 기본적인 성질은 가변값인 경우가 많지만 설정에 따라 변경 불가능한 경우도 있고 아예 불변값으로 활용하는 방안도 있다.
<참조형 데이터의 할당 예시>
var obj1 = {
a: 1,
b: 'bbb'
};
기본형 데이터와의 차이는?
-> 객체의 변수인 프로퍼티 영역이 별도로 존재한다는 점이다.
데이터 영역에 저장된 값은 모두 불변값(숫자, 문자열)이지만 변수에는 다른 값을 얼마든지 대입할
수 있기 때문에 불변하지 않다. (= 가변적이다)
<참조형 데이터의 프로퍼티 재할당 예시>
var obj1 = {
a: 1,
b: 'bbb'
};
obj1.a = 2;
위 코드와 다른점으로는 obj1의 a 프로퍼티에 숫자 2를 할당한다는 점이고, 이러한 경우를
중첩 객체(Nested object) 라고 한다.
<중첩된 참조형 데이터(객체)의 프로퍼티 할당 예시>
var obj = {
x: 3,
arr: [3, 4, 5]
};
이 상태에서 아래의 코드를 추가하여 재할당을 하게되면 어떻게 될까?
obj.arr = 'str';
어떠한 데이터에 대해 자신의 주소값을 참조하는 변수의 개수를 참조 카운트라고 한다.
해당 결과에서는 참조 카운트가 1에서 0이 되므로, 참조 카운트가 0인 메모리 주소값은
가비지 컬렉터(Garbage Collector, GC)로 수거대상이 된다.
수거 대상이 된 가비지 컬렉터는 런타임 환경에 따라 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거하며, 수거된 메모리는 다시 빈 공간이 되어 새로운 값을 할당받을 수 있다.
<변수 복사>
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
<변수 복사 이후 값 변경 결과 - 객체의 프로퍼티 변경시>
var a = 10;
var b = a;
var obj1 = { c: 10,d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2.c = 20;
<변수 복사 이후 값 변경 결과 - 객체 자체 변경시>
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2 = { c: 20, d: 'ddd' }
참조형 데이터의 '가변값'이 언제를 의미할까?
'가변'은 참조형 데이터 자체를 변경했을 때가 아닌 내부의 프로퍼티를 변경했을 때만 성립 가능하다.
위에서 언급했듯이 참조형 데이터의 '가변'은 데이터 자체가 아닌 내부 프로퍼티를 변경할 때만 성립하는데, 이는 데이터 자체를 변경하고자 한다.
다시 말해서, 새로운 데이터를 할당하고자 하면 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않는다.
그래서, 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우에 불변객체가 필요하다.
undefined - 어떤 변수에 값이 존재하지 않을 경우를 의미한다.
null - 사용자가 명시적으로 '없음'을 표현하기 위해 대입한 값이다.