자바스크립트 데이터 타입

notion image
자바스크립트는 동적 타입 언어입니다. 변수의 타입이 실행 중에 정해지며 필요에 따라 자동으로 타입이 바뀌는 암시적 타입 변환도 자주 일어납니다. 자바스크립트에서 유연성은 장점이자 의도치 않은 버그를 유발하는 원인이 되기도 합니다.
원시 타입부터 시작해서 타입에 대한 내용을 깊게 알아보려고 합니다.
자바스크립트는 크게 원시 타입객체 타입으로 나눌 수 있습니다.

원시 타입

원시 타입은 총 7가지 타입이 있습니다.
• Number • BigInt • Boolean • String
• Symbol • Null • Undefined
원시 타입 값들은 변경될 수 없으므로 불변하다고 하며 객체와 달리 속성이나 메서드를 가지지 않습니다.
불변하다는 말이 변수의 할당과 헷갈릴 수 있습니다.
  • 불변 : 한 번 만들어진 값 자체는 바뀌지 않는다.
  • 재할당 : 같은 변수가 새로운 값을 가리키도록 바꾸는 건 가능하다.
  • 10이라는 값을 수정하는 게 아니라, x다른 값(15)을 가리키도록 재할당한 것입니다.
  • 자바스크립트 문자열은 불변이므로 인덱스로 수정이 불가능합니다.
  • toUpperCase()는 기존 문자열을 바꾸지 않고 새 문자열을 만들어서 돌려줍니다.
원시 값은 값 자체를 변수에 담습니다. 하지만 객체는 참조를 담습니다.
  • 객체는 가리키는 대상 내부를 수정할 수 있기 때문에 가변하다고 합니다.
  • 원시는 내부 상태가 없고, 그 값 자체가 곧 전부라서 불변합니다.
이러한 원시값은 속성이나 메서드를 사용하려고 하면 자바스크립트는 값을 래퍼 객체로 감싸주고, 래퍼 객체는 원시값으로 작업하기 위한 여러 속성이나 메서드를 대신 제공합니다.

Number

Number 타입은 이름 그대로 숫자를 표현하는 타입입니다. 자바스크립트는 내부적으로 배정밀도 부동소수점 방식으로 숫자를 다룹니다. 배정밀도는 64비트의 메모리를 사용해 숫자 하나를 표현한다는 뜻입니다.
자바나 C# 같은 프로그래밍 언어는 정수 타입이나 부동소수점 타입을 나누지만 자바스크립트는 정수와 부동소수점을 구분하지 않고 모두 부동소수점으로 다루기 때문에 11.0은 같은 값입니다.
 
1) 정수 리터럴
정수나 부동소수점 값은 3.14와 같이 숫자로 표현할 수 있습니다. 이렇듯 타입마다 정해진 표현을 사용해 자바스크립트 코드에서 값을 직접 표현할 수 있으며, 이를 리터럴이라고 합니다.
정수 리터럴은 일반적으로 0부터 9까지 숫자를 사용해 표현할 수 있습니다.
10진수뿐만 아니라 8진수와 16진수 그리고 2진수도 표현할 수 있습니다.
  • 이때 대소문자는 구분하지 않아서 0O77 같이 사용해도 동일한 63을 얻을 수 있습니다.
 
2) 부동소수점 리터럴
부동소수점은 지수를 넣어서 표현할 수 있는데, e+1 또는 e-1 같이 지수부를 나타내는 e 또는 E 문자와 지수를 결합해 사용합니다.
주의할 점은 정수와 부동소수점 모두 64비트 메모리에 저장되기 때문에 표현할 수 있는 값의 크기에 제약이 있습니다.
자바스크립트의 부동소수점은 IEEE 754 표준을 따르는데, 이에 따르면 표현할 수 있는 값의 최대 크기는 대략 2의 1024승입니다. 이 값은 Number.MAX_VALUE로 정의되어 있고, 이보다 큰 수는 Infinity라는 특수한 값으로 변환됩니다. 최소 크기도 Number.MIN_VALUE로 정의되어 있습니다.
위 코드에서 Number.MAX_VALUE에 2를 곱하면 Infinity로 평가되지만, 비교적 작은 값을 Number.MAX_VALUE에 더하거나 빼도 Number.MAX_VALUE로 같습니다. 부동소수점은 특정 크기를 넘어가면 정확성을 잃기 때문입니다.
자바스크립트는 부동소수점 방식으로 오차 없이 표현할 수 있는 정수의 최댓값을 Number.MAX_SAFE_INTEGER로 정의해놓았습니다. 안전한 정수의 최솟값도 Number.MIN_SAFE_INTEGER로 정의되어 있습니다.
Number.MAX_VALUE와 다르게 첫 번째 줄은 false로 평가되지만, 안전한 정수 값을 벗어나면 계산이 정확해지지 않아 true로 평가됩니다.
 
3) 0.1 + 0.2, 소수점
Number 타입의 경우 앞서 말한 IEEE-754 배정밀도(64비트) 부동소수점이라 10진 소수 대부분은 유한한 길이로 표현되지 않아 근사치가 저장됩니다.
소수점을 사용할 때 단순한 계산을 하기보단 정수로 변환하는 방법을 사용해야 합니다.
 
4) BigInt
자바스크립트는 BigInt 타입을 사용해 부동소수점으로 표현하지 못 하는 큰 정수를 다룰 수 있는 방법을 제공한다. BigInt를 사용하는 방법은 정수 뒤에 n을 붙이거나 BigInt 함수를 호출하면 된다.
안전한 정수의 최댓값을 넘어서는 숫자를 계산하면 Number 타입은 true로 평가되었지만 BigInt 타입의 값을 사용하면 false로 정상적인 평가가 가능합니다.
BigInt가 Number의 최댓값을 넘어설 수 있는 이유는 상한 선이 없기 때문입니다. BigInt는 메모리와 실행 시간이 허용되는 한도까지 얼마든지 큰 정수를 표현할 수 있습니다.
이러한 BigInt 타입의 값은 Number 타입의 값과 함께 연산될 수 없다는 제약이 있습니다. 그렇기 때문에 연산 시 같은 타입으로 맞춰주어야 합니다. 또 Number의 한도를 넘어서는 BigInt의 경우 Number 타입으로 변환되면 정확성을 잃기 때문에 주의해야 합니다.

Boolean

Boolean 타입은 truefalse 두 개만 가지고 있습니다.

String

1) 템플릿 리터럴
백틱(`)을 사용하는 문자열 표현은 템플릿 리터럴이라고 불리는 특별한 형식입니다. 템플릿 리터럴을 사용하면 여러 줄을 적거나 문자열 사이에 다른 표현식을 끼워 넣을 수도 있습니다.
템플릿 리터럴에는 태그드 템플릿이라는 다른 기능도 있습니다. 태그는 표현식으로 분리된 문자열을 받아 파싱한 결과를 반환하는 함수입니다.
여기서 name${}으로 전달하는 첫 번째 값입니다. 그리고 stringsname을 기준으로 나누어지는 배열을 나타냅니다.
태그드 템플릿의 strings와 name은 `` 안의 ${}에 따라 달라집니다.
만약 myTag 안에 ${} 2개가 존재한다면 strings는 ["Hello. My name is ", ".", ""] 3개가 됩니다. 이유는 name으로 인해서 "Hello. My name is ", "." 나뉘어지고 haha로 인해서 ".", "" 나뉘어 지기 때문입니다. 만약 haha도 받고 싶다면 myTag = (strings, name, haha) => {}처럼 선언하면 됩니다.
 
2) 문자 인코딩
자바스크립트의 문자열은 유니코드의 16비트 표현 방식인 UTF-16으로 인코딩 되어 있습니다. 인코딩된 문자 하나는 메모리에서 16비트를 차지하는데, 65,536개(2^16)의 문자를 표현할 수 있는 수입니다.
영문이나 한글, 한자와 같은 대부분의 문자는 유니코드에서 기본 다국어 평면(BMP)라고 불리는 문자 집합에 속해 있고, UTF-16 인코딩 체계에서도 하나의 문자로 표현이 가능합니다. 하지만 이모지 같이 다국어 평면에 속하지 못하는 유니코드 문자는 하나의 문자로 표현할 수 없이 때문에 문자 두 개를 연이어 표현해야 합니다.
문자열의 길이는 구하는 String 객체의 length속성은 16비트 단위를 기준으로 길이를 구하기 때문에 예상과는 다른 결과를 나타냅니다.
정확한 문자의 개수를 얻고자 한다면 Array.from이나 배열 전개 문법 등을 사용해야 합니다.

Symbol

리터럴이 없고 Symbol 함수를 통해서만 생성됩니다. 또한 같은 설명 문자열을 가졌다고 하더라도 매번 고유한 Symbol 값이 생성됩니다.
이런 타입의 생긴 이유는 내부 동작에 접근하기 위해서 입니다.
자바스크립트는 객체 간 상호작용을 하기 위해 다양한 메서드를 정의합니다. 자바스크립트의 기능이 많아지면서 관련 메서드도 늘어났는데, 모든 메서드를 문자열 키로 정의하면 개발자가 정의할 수 있는 이름에 제약이 생기고, 자바스크립트의 내장 기능인지 구분이 어려워집니다.
만약 전역에 걸쳐 공용으로 사용되는 Symbol 객체가 필요하면 Symbol.for 메서드를 활용할 수 있습니다.
하지만 전역으로 공유되는 Symbol은 어디서든 생성하거나 참조가 가능하여 메모리에서 제거되지 않기 때문에 WeakMap, WeakSet과 같이 가비지 컬렉션과 관련된 기능에서는 사용이 제한됩니다.

Null

값이 null 하나 뿐이며 “값이 없음”을 나타냅니다. API에 따라 다르지만, 반환될 값이 없을 때 null 값을 반환하기도 합니다.

Undefined

Null 타입과 마찬가지로 값이 undefined 하나뿐이며, 다음과 같은 경우 반환됩니다.
  • 변수에 초깃값을 지정하지 않을 때
 
  • 객체에 속성이 없을 때
 
  • return 문이 없을 때
 
  • void 연산자를 사용할 때
 
  • 함수 인자에 값이 없을 때

객체 타입

객체 타입은 수정 가능한 다양한 속성을 가진 자료구조입니다. 각 속성은 키와 값으로 이루어져 있고 키는 문자열이나 Symbol만 허용됩니다.
객체는 원시값과 다르게 변경이 가능합니다. 속성의 값을 수정할 수 있고, 속성을 추가하거나 제거할 수 있습니다. 원시 타입을 제외한 모든 타입은 객체입니다.

정규 표현식

정규식이라고도 부르며 문자열에서 특정 패턴을 다룰 수 있는 방법입니다. 정규식 리터럴 또는 RegExp 생성자를 통해 만들 수 있습니다.
찾고자 하는 문자열 패턴을 슬래시(/)로 감싸서 사용합니다.
RegExp 생성자를 사용하면 정규식 패턴을 문자열로 전달할 수 있습니다.
/wo+w/ +는 앞 글자가 적어도 한 번 이상 등장하는 패턴을 의미합니다. w가 앞뒤로 있고 o가 다섯 번 등장하는 문자열 wooooow은 패턴과 일치하지만, 중간에 o가 없는 문자열 ww는 일치하지 않습니다.

typeof 연산자로 타입 확인하기

어떤 값의 타입이 무엇인지 알고 싶다면 typeof 연산자를 사용하면 됩니다. 주어진 값의 타입을 문자열로 반환합니다.
타입
결과
Undefined
“undefined”
Null
“object”
Boolean
“boolean”
Number
“number”
BigInt
“bigint”
String
“string”
Symbol
“symbol”
Function
“function”
Object
“object”
함수 타입은 원시 타입이 아니기 때문에 객체로 평가될 것 같지만, 예외적으로 function을 반환합니다. null 또한 object를 반환하는 것은 의도된 것이 아니라 버그입니다.
자바스크립트가 처음 개발될 때 값이 32비트 메모리에 저장되었으며, 이 메모리는 타입의 종류를 나타내는 비트와 실제값을 나타내는 비트로 이루어졌습니다. 객체의 타입 비트는 000이고, null은 메모리상에서 0x00으로 표현되기 때문에 초기 자바스크립트의 typeof 연산자는 둘을 구분하지 않고 모두 object를 반환했습니다.
자바스크립트가 널리 퍼지면서 이 버그를 바로잡을 시간이 없었기 때문에 현재까지도 하위 호환성을 위해서 남겨진 버그입니다.