자바스크립트에도 타입이 존재하나요?

자바스크립트가 다른 언어에 비해 타입 개념이 부족하긴 하지만, 자바스크립트에도 타입은 존재한답니다!

자바스크립트에서 타입은 컴퓨터(자바스크립트 엔진)나 개발자 모두에게 값을 분별해줄 수 있는 장치입니다.

좀 더 풀어서 이야기 해보면,

  • 한 친구는 수학 계산을 위해 12 라는 숫자를 사용합니다
  • 다른 친구는 화면 출력을 위해 '12'라는 문자를 사용합니다

두 친구 모두 '12'라는 값을 사용하지만 역할이 서로 다르죠? 이런 경우 '두 값은 타입이 다르다'라고 이야기 할 수 있습니다.

그럼 지금부터 자바스크립트에는 어떤 타입이 있는지 알아보겠습니다.

타입

내장 타입

자바스크립트에는 총 7가지의 내장 타입이 존재합니다.

  • null
  • undefined
  • number
  • string
  • boolean
  • object
  • symbol (ES6+)

타입은 어떻게 찾죠?

값의 타입은 typeof로 찾아낼 수 있습니다.

주의 😱

  • nulltypeof 결과는 좀 특별하게 null이 아니라 object로 나옵니다.

그 외 🧐

  • typeoffunction이라는 타입도 존재하지만, function 타입은 내장 타입이 아닌 object의 하위 타입입니다. (호출이 가능한 객체)
  • 배열(Array)도 object의 하위 타입입니다. 따로 타입은 없고 object로 나옵니다. (숫자 인덱스를 키 값으로 가지고 있는 객체)

    typeof 12               // "number"
    typeof "12"             // "string"
    typeof false            // "boolean"
    typeof undefined        // "undefined"
    typeof { count: 12 }    // "object"
    typeof Symbol()         // "symbol"
    typeof null             // "object"  * null의 타입은 null이 아니에요!
    typeof function() {}    // "function"
    typeof []               // "object

특수한 타입

"undefined"는 특수한 타입으로, 두 가지 경우가 있습니다.

  1. 변수에 값이 없어서 생기는 타입
  2. 선언 자체를 안해서 생기는 타입

즉 같은 "undefined"라도 두 가지 경우로 나뉠 수 있는 것이죠.

let a;
a;       // "undefined"
b;       // ReferenceError: b is not defined

위의 코드에서 변수에 값이 없는 경우는 "undefined"라는 값을 주지만, 선언 자체를 안한 경우는 구문에서 나온 에러의 내용을 줍니다.

그럼 둘의 실제 타입을 찍어볼까요?

let a;
typeof a;     // "undefined"
typeof b;     // "undefined"

이렇듯 a와 달리 b 자체는 구문 에러가 떨어져도 둘 다 "undefined"의 타입을 가지게 되는 것이죠. 그래서 선언하지 않은 변수에 typeof를 붙이면 안전하게 코드가 돌아갈 수 있도록 만들어 줍니다.

배열(Array)

자바스크립트의 배열은 한 배열에 어떤 타입의 값이라도 담을 수 있습니다.

const array = [0, true, "aa", [2]];

그리고 배열은 빈 슬롯도 둘 수 있지만 혼란을 일으킬 수 있어 조심해야 합니다.

c[0] = 2
c[4] = 3
c       // [2, <3 empty items>, 3 ]
c[2]     //undefined
c.length   //5

배열 자체도 객체여서 키/프로퍼티에 문자열을 넣을 수 있습니다. 그렇지만 배열의 length가 늘어나지 않습니다.

let meal = [];
meal["morning"] = "cereal";
meal.morning   // "cereal";
meal.length    // 0 *문자열을 키값으로 설정하면 배열의 길이가 늘어나지 않습니다.

그리고 키값의 문자열 값이 숫자로 변경되면 숫자키를 사용한 효과를 내니 조심해야 합니다!

let calander = [];
calander["30"] = "holiday";
calander; // [ <30 empty items>, 'holiday' ]

문자열(String)

문자열은 얼핏 보기엔 문자의 배열 같지만 사실은 배열과 많이 다르답니다!

우선 문자열과 문자 배열의 비슷한 점은, 둘 다 length 프로퍼티나 indexOf()로 접근이 가능하다는 것입니다. 그래서 문자열은 유사 배열이죠.

문자열과 문자 배열의 차이점

let name = "Cat";
let name_array = ["C","a","t"];
name[2] = "r";
name_array[2] = "r";
name;           //'Cat'
name_array;     //[ 'C', 'a', 'r' ]

위 코드를 보면 문자열은 불변 값이지만, 배열은 가변 값입니다.

그래서 수정사항을 바로 적용하는 배열과 달리, 문자열은 변경사항이 있으면 값을 바로 수정하지 않고 새로운 문자열을 만든 후 그것을 반환하는 식이죠.

숫자(Number)

자바스크립트에서는 모든 숫자를 number 타입 하나로 표시합니다. 즉 정수나 소수점이 있는 숫자 모두 number 입니다.

숫자는 기본적으로 10진수로 표현합니다.

let index = 12;

소수의 경우 소숫점 앞이 0이면 생략이 가능하고, 소숫점 이하가 0일 때도 생략이 가능합니다.

let number1 = .82  //0.82
let number2 = 52.  //52.0  <- 실제로는 저렇게 표기하면 다른 사람들이 혼동이 올 수 있으니 좋지 않아요!

숫자 값은 Number.prototype으로 접근할 수 있습니다.

객체가 만들어지기 위해서는 자신을 만드는 데 사용된 원형인 프로토타입 객체를 이용해야 하는데, 그 원형인 프로토타입 객체를 연결하는 링크를 prototype이라고 정의합니다.

그래서 실제로는 number1에서 toString()을 선언한적이 없지만, prototype을 통해 상위 객체에서 toString 을 찾고 toString을 발견하면 그 메소드를 실행시켜 주는 것이죠.

숫자 값이지만 자바스크립트는 원시 값들을 객체 래퍼로 자동으로 감쌀 수 있기 때문에, 객체처럼 prototype에 접근이 가능합니다.

let number1 = 1234;
number1.toString() //"1234";

특수값

undefined

undefined는 '값이 없다'라는 의미를 가지고 있지만 식별자로 사용이 가능하므로 주의해야 합니다.

undefined = 2;
console.log(undefined);  //2

void 연산자

void는 항상 결과값을 undefined로 만들어 줍니다.

let number = 20;

console.log(void number, number); //"undefined", 20;

실질적으로는 잘 쓰지 않지만, 대표적으로 a 태그에 void를 사용해서 링크를 무력화하는 용도로 사용할 수 있습니다.

<a href="javascript:void(0)">아무것도 안움직임</a>

NaN

NaN은 Not a Number의 약자입니다. 그러나 막상 typeof를 찍으면 NaNnumber 타입으로 나오죠... (Not a Number is number...?)

그러므로 '숫자가 아니다'로 이해하기보다는 '유효하지 않은 숫자'로 이해하는 것이 더 쉽답니다!

let notNumber = 20 / a;
typeof notNumber;   //"number"

NaN은 어떤 NaN과도 동등하지 않기 때문에 비교를 위해서는 isNaN()을 사용하시면 됩니다.

notNumber === NaN; // false
isNaN(notNumber); // true

주의 😱

실제로 isNaN은 인자 값이 숫자인지 여부를 평가합니다. 그래서 아래와 같은 오류가 발생하곤 하죠.

isNaN("NaN?")//true

만약 실제로 NaN만 필터링하고 싶다면, typeof를 했을 때 반드시 number인지 체크해주어야 위와 같은 불상사를 막을 수 있습니다.

무한대

자바스크립트에서 0으로 나누면 Infinity 값이 나오며, 그 외 최대로 표현 가능한 숫자식(Number.MAX_VALUE) 이상을 넘어가면 무한대로 나옵니다.

let a = 1/0  //Infinity

만약 최대로 표현할 수 있는 숫자식보다 더 큰 정수를 다룬다면 BigInt를 사용하는 것을 추천합니다!

Number.MAX_SAFE_INTEGER
//9007199254740991
Number.MAX_SAFE_INTEGER + 1;
//9007199254740992
Number.MAX_SAFE_INTEGER + 2;
//9007199254740992

const maxValue = BigInt(Number.MAX_SAFE_INTEGER);
const maxPlusOne = maxValue + 1n;
// 9007199254740992n
 
const theFuture = maxValue + 2n;
// 9007199254740993n

위와 같이 최대로 표현 가능한 숫자식에 계산을 하면 원하는 값이 나오지 않는 경우가 있는데요. 이런 경우 BigInt를 사용하면 최대 표현할 수 있는 숫자식을 넘어가도 정상적인 계산값이 나온답니다.

0

자바스크립트에는 0도 있지만 -0도 존재하며, 특정 수식도 -0로 표현됩니다.

let zero = 0/-5;
zero;  // -0

하지만 -0을 문자열화하면 항상 0이 되죠.

zero.toString() // "0"

값과 레퍼런스

값의 타입으로 값을 복사하는지 혹은 레퍼런스를 복사하는지 정해집니다.

값 복사

객체를 제외한 단순값은 언제나 값을 복사해옵니다.

독립적으로 각자의 값을 사본으로 가지고 있기 때문에, 값을 변경해도 다른 변수에 영향을 주지 않죠.

let count = 15;
let backupCount = count;

backupCount++;

count;           //15
backupCount;     //16

레퍼런스 복사

객체 같은 합성값은 레퍼런스를 복사해옵니다.

즉, 값을 복사하고 있는 것이 아니라 값을 동등하게 참조만 하고 있기 때문에 독립적이지 않죠.

그래서 실제 값이 영향을 받으면 레퍼런스로 접근한 변수들은 갱신된 값을 보여줍니다.

let holiday = ["9월 3일", "10월 5일", "10월 30일"];
let backupHoliday = holiday;
backupHoliday.push("12월 10일");

holiday;           //["9월 3일", "10월 5일", "10월 30일", "12월 10일"];
backupHoliday;     //["9월 3일", "10월 5일", "10월 30일", "12월 10일"];

자바스크립트는 포인터와 같은 개념이 없기 때문에, 레퍼런스로 다른 변수의 레퍼런스의 참조를 변경할 수는 없습니다.

let holiday = ["9월 3일", "10월 5일", "10월 30일"];
let backupHoliday = holiday;
backupHoliday = ["10월 3일", "11월 3일", "12월 25일"]; //이건 holiday의 레퍼런스를 참조해서 변경하는게 아니라 backupHolyday가 참조하는 래퍼런스를 새로 할당받는 상황입니다.

holiday;           //["9월 3일", "10월 5일", "10월 30일"];
backupHoliday;     //["10월 3일", "11월 3일", "12월 25일"];

안전한 객체 복사

이렇듯 객체는 실제 값이 변경되는 문제가 있기 때문에, 자신이 원하는 형태로 작동하지 않을 수 있습니다. 그런 경우에는 어떻게 해야 될까요?

그래서 객체를 복사할 때 얕은 복사(shallow copy)와 깊은 복사(deep copy)라는 두 가지 방법을 사용합니다.

간단하게만 설명을 하자면,

얕은 복사는 상위 객체만 새로 생성해서 값을 복사하고 내부 객체들은 주소를 참조하는 형식입니다.

깊은 복사는 상위 객체도 새로 생성할 뿐만 아니라 내부의 객체들도 새로 생성하여 값을 복사합니다. 이런 식으로 객체를 복사해서 사용할 경우, 원본 값을 변경하지 않고 안전하게 복사할 수 있는 것이죠!

마치며

자바스크립트에는 타입이 존재하고 여러 값들이 존재하지만, 타입 보장이 잘 안 되기 때문에 사람들이 오용하는 경우가 많은데요. 만약 자바스크립트를 계속해서 사용하실 거라면 이런 기초적인 것들부터 주의하면서 코딩해나가는 것이 매우 중요합니다.

이처럼 타입이 잘 보장되지 않는 자바스크립트의 단점을 보완하기 위해, 연애의 과학 팀에서는 타입스크립트라는 언어를 사용하고 있습니다. 이를 통해 사전에 타입을 보장하여 자바스크립트에서 중요한 타입을 오용하는 것을 미리 방지할 수 있도록 셋팅을 해주는 것이죠!

혹시 타입스크립트를 처음 접해보셨거나, 아직 필요하다고 느낀 적이 없으시다구요? 그렇다면 다음 글을 읽어보시는 걸 추천해드려요!

🙌
연애의 과학 프로덕트팀에서는 프론트엔드 엔지니어를 채용중이에요 프로덕트와 리엑트, 웹을 사랑하고 능력있는 Pro-duck들이 모여, 연애의 과학을 만들고 있어요. 지금 연애의 과학팀에 합류하세요! → 채용공고 바로가기