[ You Don’t Know JS 정리 ]

1. 타입


1.1 타입, 그 실체를 이해하자
1.2 내장 타입
1.3 값은 타입을 가진다
      1.3.1 값이 없는 vs 선언되지 않은
      1.3.2 선언되지 않은 변수


1.1 타입, 그 실체를 이해하자

  • 타입 : 자바스크립트 엔진, 개발자 모두에게 어떤 값을 다른 값과 분별할 수 있는, 고유한 내부 특성의 집합

  • 거의 모든 자바스크립트 프로그램에서 강제 변환이 일어나므로 타입을 확실하게 인지하고 사용하는 것이 중요

1.2 내장 타입

  • 7가지 내장 타입 (es6 기준)

    • null : primitive type
    • undefined : primitive type
    • boolean : primitive type
    • number : primitive type
    • string : primitive type
    • object : reference type
    • symbol : primitive type
  • 값 타입은 typeof 연산자로 알 수 있다.

    1
    2
    3
    4
    5
    typeof undefined === "undefined"; // true
    typeof true === "boolean"; // true
    typeof 42 === "number" // true
    typeof {life : 42} === "object" // true
    typeof Symbol() === "symbol" // true

하지만 내장 타입과 1:1로 정확하게 매치되지는 않는다.

1
typeof null === "object" // true

null은 ‘falsy’한 유일한 원시 값이지만, 타입은 ‘object’인 특별한 존재다.

그래서 타입으로 null 값을 정확히 확인하려면 조건이 하나 더 필요하다.

1
2
var a = null;
(!a && typeof a === "object") // true

typeof가 반환하는 문자열은 하나 더 있다.

1
typeof function a(){...} === "function" // true

마치 function이 최상위 레벨의 내장 타입처럼 보이지만 실제로는 ‘object’의 하위 타입이다. => 호출 가능한 객체

따라서 함수에 프로퍼티를 둘 수 있고, 함수에 선언된 인자 개수는 함수 객체의 length 프로퍼티로 알 수 있다.

1
2
3
4
function a(a,b) {
/* ... */
}
a.length // 2

1.3 값은 타입을 가진다.

  • 값에는 타입이 있지만, 변수에는 타입이란 없다.
  • 자바스크립트는 타입 강제를 하지 않는다. 변수값이 처음에 할당된 값과 동일한 타입일 필요 없다.
  • typeof의 연산자의 반환 값은 언제나 문자열이다. ex) “string”, “number”

1.3.1 값이 없는 vs 선언되지 않은

  • “undefined”(값이 없는)과 “undeclared”(선언되지 않은)을 동의어처럼 생각하기 쉬운데 둘은 완전히 다른 개념
  • undefined : 접근 가능한 스코프에 변수가 선언되었으나 현재 아무런 값도 할당되지 않은 상태를 가리킨다
  • undeclared : 접근 가능한 스코프에 변수 자체가 선언조차 되지 않은 상태
    1
    2
    3
    var a;
    a; // undefined
    b; // ReferenceError : b is not defined

1.3.2 선언되지 않은 변수

  • 여러 스크립트 파일의 변수들이 전역 네임스페이스를 공유할 때, typeof가 유용

  • typeof 로 전역 변수 체크

    • 프로그램의 ‘디버그 모드’를 DEBUG라는 전역 변수로 조정한다고 쳤을 때

    • 콘솔 창에 메시지 로깅 등 디버깅 작업을 수행하기 전, 이 변수의 선언 여부를 체크

    • 최상위 전역 스코프에 var DEBUG = true 라고 ‘debug.js’ 파일에만 선언하고, 개발/테스트 단계에서 이 파일을 브라우저가 로깅하기만 하면 된다.

    • 그러나 나머지 어플리케이션 코드에서 ReferenceError가 나지 않게 하려면 조심해서 DEBUG 전역 변수를 체크해야 한다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      [utility.js]
      function sum(a,b) {
      return a + b;
      }

      function product(a,b) {
      return a * b;
      }
      export {product, sum};

      [app.js]
      import {product, sum} from 'utility';

      console.log(product(1,2)) //2
      console.log(sum(1,2)) //3

      // 에러 발생
      if (DEBUG) {
      console.log("디버깅 시작")
      }

      // 안전하게 존재 여부 체크
      if (typeof DEBUG !== "undefined") {
      console.log("디버깅 시작")
      }

    • typeof 없이 전역 변수를 체크하는 다른 방법

      • 전역 변수가 모두 전역 객체(브라우저는 window)의 프로퍼티라는 점을 이용하는 것

        1
        2
        3
        if (window.DEBUG) {
        // ...
        }
      • 하지만 가급적 삼가는 것이 좋다.

        • 전역 변수를 꼭 window 객체로만 호출하지 않는 다중 자바스크립트 환경(브라우저뿐만 아니라 서버에서 실행되는 node.js)이라면 더욱

    • 전역 변수를 사용하지 않을 때

      • 일부 개발자들은 이런 설계 방식이 그다지 바람직하지 않다고 말한다.

      • ex) 다른 개발자가 유틸리티 함수를 자신의 모듈/프로그램에 카피 앤 페이스트하여 사용하는데, 가져다 쓰는 프로그램에 유틸리티의 특정 변숫값이 정의되어 있는지 체크해야하는 상황

        1
        2
        3
        4
        5
        6
        7
        8
        function doSomethingCool() {
        var helper =
        (typeof FeatureXYZ !== "undefined") ?
        FeatureXYZ :
        function() { /*기본 XYZ 기능*/};

        var val = helper();
        }
      • doSomethingCool함수는 FeatureXYZ 변수가 있으면 그대로 사용하고, 없으면 함수 바디를 정의한다.

      • 이렇게 해야 다른 사람이 카피 앤 페이스트를 해도 안전하게 FeatureXYZ가 존재하는지 체크할 수 있다.


    • 의존성 주입 설계 패턴

      • 명시적으로 의존 관계를 전달
        1
        2
        3
        4
        5
        6
        function doSomethingCool(FeatureXYZ) {
        var helper = FeatureXYZ ||
        function() {/*기본 XYZ 기능*/};

        var val = helper();
        }