프론트엔드/JavaScript

[JavaScript] 자료형 (Data Type)

데이터 타입은 프로그래밍 언어가 사용할 수 있는 데이터의 종류를 의미합니다.

 

이 글을 통해 먼저 원시 타입의 종류에 대해 알아보고자 합니다.

불변성의 관점에서 원시 타입과 참조 타입을 알아볼 예정입니다.

그러면 왜 타입마다의 데이터 전달 방식이 다른 지도 함께 이해될 것입니다.

 


1. 원시 타입 (Primitive Type)

원시 타입은 immutable합니다. 즉 값이 생성된 후에 그 값을 변경할 수 없습니다.

원시 타입은 값을 복사하여 전달(pass by value)합니다.

 

각 타입마다 할당받는 메모리 크기가 다릅니다.

이를 통해 한정된 메모리 공간을 효율적으로 사용할 수 있습니다.

그리고 할당 가능한 값의 유효 범위를 한정할 수도 있습니다.

 


1.1 number

C 등 대부분 언어는 다양한 숫자 타입(int, long, float, double)을 가집니다.

하지만 JavaScript는 모든 수를 64비트 부동 소수점 방식으로 나타냅니다.

 

64비트 부동 소수점 방식(IEEE 754) : 가수부(fraction)은 실수의 밑 값을, 지수부(exponent)은 실수의 지수 값을 나타냅니다.
64비트 부동소수점 방식의 범위

 

JavaScript의 MAX_VALUE 값은 2^1024 (1.79E+308)입니다.

이는 부동 소수점 방식의 최대 값과 같습니다.

 

하지만 JavaScript의 MAX_SAFE_INTEGER 값은 2^53 - 1 (9007199254740991)로 훨씬 낮습니다.

JavaScript는 안전 범위보다 큰 수를 변수에 저장할 수 있지만 정확성을 보장할 수 없습니다.

 


1.2 bigInt

bigInt 타입은 number 타입의 안전 범위(2^53 - 1) 보다 큰 정수를 저장하고 연산할 수 있습니다.

bigInt는 정수 끝에 n을 추가하거나 생성자를 호출하는 방식으로 생성이 가능합니다.

 

다만 bigInt와 number를 혼합해 사용할 수 없고 Math 객체의 메서드를 사용할 수 없습니다.

const BiggestInt1 = 9007199254740991n;
const BiggestInt2 = BigInt(9007199254740991);

 


1.3 string

let str = 'Hello';
str = 'world';

JavaScript의 문자열 값은 immutable합니다.

하지만 위 코드는 정상적으로 실행됩니다. 어떻게 가능할까요?

 

문자열 자체는 변경 불가능한 것이 맞습니다.

2번째 구문이 실행되면 기존의 ‘Hello’가 수정되는 것이 아닌, 새로운 문자열 ‘world’가 생성됩니다.

그리고 변수 str는 ‘Hello’ 대신 ‘world’를 가리키도록 변경되는 것입니다.

 


const str = 'string';

for (let i = 0; i < str.length; i++) {
  console.log(str[i]);
}

또 문자열은 유사 배열이므로 배열처럼 인덱스를 통해 접근할 수도 있습니다.

 

유사 배열

유사 배열은 배열처럼 보이지만 사실 key가 숫자이고 length 값을 가지고 있는 객체입니다.

대표적인 예시로 객체, NodeList, HTMLCollection 등이 있습니다.

 

그래서 배열 메서드(forEach, map, filter, reduce..)를 사용할 수 없습니다.

대신 Array.from()을 통해 유사 배열을 배열로 복사하면 메서드를 사용할 수 있게 됩니다.

 


1.4 boolean

JavaScript의 불리언 타입은 true, false로 나타냅니다.

JavaScript에서 빈 문자열, 0, null, undefined, NaN 등의 값들은 false로 간주됩니다.

 


1.5 undefined

undefined 타입 안에는 undefined 값이 유일합니다.

JavaScript 엔진은 선언 이후 값이 할당되지 않은 변수를 undefined로 초기화합니다.

 

이러한 의미 때문에 개발자가 의도적으로 undefined를 사용하는 것을 권장하지 않습니다.

만약 변수의 값이 없다는 것을 명시하고 싶다면 null을 사용하면 됩니다.

 


1.6 null

null 타입 안에는 null 값이 유일합니다.

null은 보통 변수에 값이 없다는 것을 명시하는 용도로 사용됩니다.

 

null의 할당은 변수에 연결된 메모리 주소 참조를 제거하겠다는 의미이기도 합니다.

이후 JavaScript 엔진은 참조하지 않는 메모리 영역에 대해 가비지 컬렉션을 수행하게 됩니다.

 


1.7 symbol

심볼은 ES6에서 새롭게 추가된 원시 타입입니다.

심볼은 이름의 충돌 위험이 없는 고유한 값을 만들기 위해 사용합니다.

그래서 이를 객체의 프로퍼티 키로 활용할 수도 있습니다.

const key = Symbol('key');
console.log(typeof key); // symbol

const obj = {};
obj[key] = 'value';
console.log(obj[key]); // value

 


2. 참조 타입 (Reference type, Object type)

원시 타입을 제외한 거의 모든 것(객체, 배열, 함수..) 참조 타입으로 볼 수 있습니다.

 

 

(참조 타입은 Heap 영역에 저장되고 참조값(address)을 통해 접근할 수 있습니다.

 

참조 타입은 프로퍼티를 추가, 변경, 삭제가 가능한 mutable 특징을 가집니다.

이러한 변화 가능한 성질 때문에 필요한 메모리 공간을 예측하기 어렵습니다.

 

참조 타입을 변수에 할당하면 참조 타입은 메모리의 힙(Heap) 영역에 저장됩니다.

그리고 변수는 그 참조값(address)을 갖게 되는 것입니다.

그래서 객체는 값을 전달할 때 값을 복사하지 않고 참조값을 전달(pass by reference)합니다.

 

(메모리 구조에 대해선 추후 자세히 다룰 예정입니다.)