“typeof Null”의 역사

이 문서는 http://2ality.com/2013/10/typeof-null.html를 번역한 것입니다.

자바스크립트에서 typeof null의 결과는 ‘object’이고, 이 의미는 null을 객체라고 잘못 알려주고 있다. null은 원시타입이다. 이것은 불행하게도 수정될 수 없는 버그다. 왜냐하면 이게 수정되면 기존 코드에 부정적인 영향을 미칠 수 있기 때문이다. 이 버그의 역사에 대해 살펴보자.

typeof null 버그는 자바스크립트의 첫 번째 버전의 잔존물이다. 해당 버전에서 변수의 값은 32비트 단위로 저장되었는데, 1-3 비트에서는 타입 태그로 변수의 타입에 대해 저장했고, 나머지 비트에는 실제 데이터로 구성되었다. 아래는 타입 태그에 해당되는 5종류다.

  • 000: 객체. 데이터는 객체에 대한 참조다.
  • 1: 정수. 데이터는 31비트의 부호를 가진 정수다.
  • 010: 실수. 데이터는 double 부동 소수점에 대한 참조다.
  • 100: 문자. 데이터는 문자에 대한 참조다.
  • 110: 참/거짓. 데이터는 참/거짓이다.

즉, 가장 낮은 비트는 1이었고, 그 타입 태그의 길이는 오직 1비트였다. 혹은 0인데, 그 길이는 4 종류의 타입을 표현하기 위해 2개의 비트가 추가로 있는 3비트였다.

2가지 값은 특별했다.

  • undefined (JSVAL_VOID)은 정수 −230였다. (정수 범위를 벗어난 숫자).
  • null (JSVAL_NULL)은 기계어 코드 NULL 포인터였다. 혹은 객체 타입과 0인 참조였다.

이제서야 왜 typeof null의 결과가 객체라고 나오는지 명확해졌다. 타입 태그를 조사하니 그 값이 “객체”였기 때문이다. 아래의 코드는 typeof 명령어가 정의된 엔진의 코드다.

    JS_PUBLIC_API(JSType)
    JS_TypeOfValue(JSContext *cx, jsval v)
    {
        JSType type = JSTYPE_VOID;
        JSObject *obj;
        JSObjectOps *ops;
        JSClass *clasp;

        CHECK_REQUEST(cx);
        if (JSVAL_IS_VOID(v)) {  // (1)
            type = JSTYPE_VOID;
        } else if (JSVAL_IS_OBJECT(v)) {  // (2)
            obj = JSVAL_TO_OBJECT(v);
            if (obj &&
                (ops = obj->map->ops,
                 ops == &js_ObjectOps
                 ? (clasp = OBJ_GET_CLASS(cx, obj),
                    clasp->call || clasp == &js_FunctionClass) // (3,4)
                 : ops->call != 0)) {  // (3)
                type = JSTYPE_FUNCTION;
            } else {
                type = JSTYPE_OBJECT;
            }
        } else if (JSVAL_IS_NUMBER(v)) {
            type = JSTYPE_NUMBER;
        } else if (JSVAL_IS_STRING(v)) {
            type = JSTYPE_STRING;
        } else if (JSVAL_IS_BOOLEAN(v)) {
            type = JSTYPE_BOOLEAN;
        }
        return type;
    }

위의 코드가 수행되는 단계는 아래와 같다.

  • (1)에서 첫번째로 엔진은 v라는 값이 undefined(VOID)인지 검사한다. 이 검사는 동등 연산자를 통해 값을 비교하여 수행된다.

    #define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID)

  • (2)에서 해당 값이 객체 태그를 가졌는지 검사한다. 만약 (3)에서처럼 호출이 가능하거나 (4)에서처럼 내부속성인 [[Class]]의 값이 함수를 의미하면 v는 함수다. 그렇지 않으면 객체를 의미한다. 이 부분이 바로 typeof null의 값이 할당되는 부분이다.

  • 후속 검사는 숫자, 문자, 참/거짓을 위함이다. 더이상 아래와 같은 null에 대한 명시적인 검사는 없다.

    #define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL)

이것은 매우 명백한 버그처럼 보일지 모르겠지만, 자바스크립트의 첫 번째 버전이 매우 짧은 시간에 탄생했다는 걸 잊지마라.

(역자주: 자바스크립트는 10일만에 설계가 완성됨)

Written on January 1, 2018
Translated by 송헌용