Javascript 데이터 타입
데이터 타입의 종류
데이터 타입은 크게 두가지가 존재한다. 기본형과 참조형이다.
기본형에는 숫자, 문자열, 불리언, null, undefined 등 이 있으며 ES6에서는 심볼이 추가되었다.
참조형은 객체가 있고, 배열, 함수, 날짜, 정규표현식 등과 ES6에서 추가된 Map, WeakMap, Set, WeakSet 등이 객체의 하위 분류에 속한다.
어떤 기준으로 기본형과 참조형을 구분할까?
일반적으로 기본형은 할당이나 연산 시 복제되고 참조형은 참조된다고 알려져 있다.
엄밀히 말하면 둘 모두 복제를 하긴한다. 다만 기본형은 값이 담긴 주소값을 바로 복제하는 반면 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다는 점이 다르다.
기본형은 불변성을 띈다. 혼란스러울 수 있는데 기본형인 숫자 10을 담은 변수 a에 다시 숫자 15를 담으면 a의 값은 문제 없이 15로 변하는데 변하지 않는다는게 어떤 의미일지 궁금하다.
불변성을 이해하려면 개략적으로나마 메모리와 데이터에 대한 지식이 필요하고, '식별자'와 '변수'의 개념을 구분할 수 있어야 한다.
메모리와 데이터
컴퓨터는 모든 데이터를 0 과 1 로 기억한다. 이를 표현할 수 있는 하나의 작은 메모리 조각을 비트라고 한다.
메모리는 많은 비트들로 구성되어 있는데 각 비트는 고유한 식별자를 통해 위치를 확인할 수 있다.
하지만 0이나 1만 표현할 수 있는 비트 단위로 위치를 확인하는 것은 매우 비효율적이다. 이를 몇 개씩 묶어 하나의 단위로 여긴다면 표현할 수 있는 값도 늘어나며 검색시간을 줄일 수도 있다.
이와 동시에 낭비되는 비트가 생기기도 한다. 자주 사용하지 않을 데이터를 표현하기 위해 빈 공간을 남겨놓기 보다는 표현 가능한 개수에 어느 정도 제약이 따르더라도 크게 문제가 되지 않을 적정한 공간을 묶는 것이 더 낫다. 이래서 바이트가 생겨났다.
1바이트는 8개의 비트로 구성되어 있다. 1바이트는 총 256개의 값을 표현할 수 있다.
C, C++, 자바 등의 정적 타입 언어는 메모리의 낭비를 최소화하기 위해 데이터 타입별로 할당할 메모리 영역을 2바이트, 4바이트 등으로 나눠 정해놓았다.
하지만 메모리 용량이 과거보다 월등히 커진 상황에서 등장한 자바스크립트는 상대적으로 메모리 관리에 대한 압박에서 자유로웠다.
그래서 메모리 공간을 좀 더 넉넉하게 할당했다.
숫자의 경우 정수형인지 부동소수형인지를 구분하지 않고 64비트, 즉 8바이트를 확보한다. 덕분에 개발자가 형변환을 걱정해야 하는 상황이 훨씬 덜 발생하게 되었다.
모든 데이터는 바이트 단위의 식별자, 즉 메모리 주솟값을 통해 서로 구분하고 연결할 수 있다.
변수와 데이터 할당
var a;
이는 선언할 때는 undefined 이더라도 나중에 다른 값으로 바꾸면 된다. 즉 변수는 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇 이라고 생각할 수 있다.
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
데이터 | 이름 : a 값 : |
명령을 받은 컴퓨터는 메모리에서 비어있는 공간 하나를 확보한다. 여기서 1003번은 임의로 정한 것이다.
이 공간의 이름(식별자)을 a라고 지정한다. 여기까지가 변수 선언 과정이다.
이후 사용자가 a에 접근하고자 하면 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해 해당 공간에 담긴 데이터를 반환할 것이다.
var a;
a = 'abc';
var a = 'abc';
위 코드에서 1, 2 줄처럼 사용하든 4 줄 처럼 사용하든 자바스크립트 엔진은 결국 같은 동작을 수행한다.
메모리에서 비어있는 공간을 확보하고 그 공간의 이름을 설정하는 과정은 아까와 같다.
이 다음은 a라는 이름을 가진 주소를 검색해서 그곳에 'abc'를 할당하면 된다.
하지만 실제로 'abc'를 직접 저장하지 않는다. 데이터를 저장하기 위한 별도의 메모리 공간을 다시 확보해서 문자열 'abc'를 저장하고, 그 주소를 변수영역에 저장하는 식으로 이루어진다.
변수 영역
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
데이터 | 이름 : a 값 : @5004 |
데이터 영역
주소 | ... | 5002 | 5003 | 5004 | 5005 | ... |
데이터 | 'abc' |
- 변수 영역에서 빈 공간(@1003)을 확보한다.
- 확보한 공간의 식별자를 a로 지정한다.
- 데이터 영역의 빈 공간(@5004)에 문자열 'abc'를 저장한다.
- 변수 영역에서 a라는 식별자를 검색한다.(@1003)
- 앞서 저장한 문자열의 주소(@5004)를 @1003의 공간에 대입한다.
변수 영역에 값을 직접 대입하지 않는 이유는 무엇일까?
이는 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더 효율적으로 관리하기 위해서이다.
자바스크립트는 숫자형 데이터에 대해 64비트(8바이트)를 확보한다. 반면 문자열은 정해진 규격이 없다. 한 글자마다 영어는 1바이트, 한글은 2바이트로 각각 필요한 메모리 용량이 가변적이며 전체 글자수 역시 가변적이기 때문이다.
만약 미리 확보한 공간 내에서만 데이터 변환을 할 수 있다면 변환한 데이터를 다시 저장하기 위해서는 확보된 공간을 변환된 데이터의 크기에 맞게 늘리는 작업을 해야한다.
이러면 컴퓨터가 처리해야 할 연산이 많아질 수 밖에 없다.
효율적으로 문자열 데이터의 변환을 처리하려면 변수와 데이터를 별도의 공간에 나누어 저장하는 것이 최적이다.
문자열 'abc'의 마지막에 'def'를 추가하려고 하면 'abc'가 저장된 공간에 'abcdef'를 할당하는 대신 'abcdef'라는 문자열을 새로 만들어 별도의 공간에 저장하고 그 주소를 변수 공간에 연결한다. 기존 문자열에 어떤 변환을 가하든 상관없이 무조건 새로 만들어 별도의 공간에 저장한다.
변수영역
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
데이터 | 이름 : a 값 : @5005 |
데이터 영역
주소 | ... | 5002 | 5003 | 5004 | 5005 | ... |
데이터 | 'abc' | 'abcdef' |
이렇게 변수 영역과 데이터 영역을 분리하면 중복된 데이터에 대한 처리 효율이 높아진다.
정리
자바스크립트 데이터 타입에는 기본형과 참조형이 있다.
기본적으로 기본형은 불변값이고 참조형은 가변값이다.
변수는 변경 가능한 데이터가 담길 수 있는 공간이고, 식별자는 그 변수의 이름을 말한다.
변수를 선언하면 컴퓨터는 우선 메모리의 빈 공간에 식별자를 저장하고 그 공간에 자동으로 undefined를 할당한다.
이후 그 변수에 기본형 데이터를 할당하려고 한다면 별도의 공간에 데이터를 저장하고 그 공간의 주소를 변수의 값 영역에 할당한다.
참조형 데이터를 할당할 경우 컴퓨터는 참조형 데이터 내부 프로퍼티들을 위한 변수 영역을 별도로 확보해 확보된 주소를 변수에 연결하고 다시 앞서 확보한 변수 영역에 각 프로퍼티의 식별자를 저장하고, 각 데이터를 별도의 공간에 저장해서 그 주소를 식별자와 매칭시킨다.
항당과정에서 기본형과 차이가 발생한 이유는 참조형 데이터가 여러 개의 프로퍼티를 모은 그룹이기 때문이다.
이 차이로 인해 참조형 데이터를 가변값으로 여겨야만 하는 상황이 발생한다.
참조형 데이터를 가변값으로 여겨야 하는 상황에도 불변값으로 사용할 수 있다.
내부 프로퍼티들을 일일이 복사(깊은 복사), 혹은 라이브러리를 사용한다.
없음을 나타내는 값은 두 가지가 있는데 undefined는 어떤 변수에 값이 존재하지 않을 경우를 의미하고 null은 사용자가 명시적으로 없음을 표현하기 위해 대입한 값이다. 본래의 의미에 따라 사용자가 없음을 표현하기 위해 명시적으로 undefined 를 대입하는 것은 지양해야 한다.