Перевод статьи "The history of “typeof null”".
В JavaScript typeof null
вернёт "объект", так что мы можем предположить,
что null - это объект. Однако, это не так.
Это ошибка, и она, к сожалению, не может быть исправлена, потому что это бы поломало существующий код.
Давайте рассмотрим историю этой ошибки.
Ошибка "typeof null" - это наследие первой версии JavaScript. В этой версии значения хранились в 32 битах, которые состояли из небольшого тега типа значения (1-3 бита) и данных значения. Типы значения хранились в младших битах. Их было пятеро:
000
: object. Данные - это ссылка на объект.1
: int. Эти данные представляют собой 31-битное целое число со знаком.010
: double. Данные – ссылка на число с плавающей точкой двойной точности.100
: string. Ссылка на строку.110
: boolean. Логические.
То есть самый младший бит был либо 1
, тогда тип имеет длину в один бит.
Или он 0
, тогда тип был длиной в три бита, обеспечивая два дополнительных бита для четырех типов.
Также было два специальных значения:
undefined
(JSVAL_VOID
) был целым числом значением −2 в 30-ой степени.null
(JSVAL_NULL
) был машинным указателемNULL
(тип - объект, указательNULL
).
Теперь должно быть понятно, почему typeof null
был объектом:
он смотрел на биты типа, по которым 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)
проверяем на значение undefined
(VOID
). На самом деле,
за JSVAL_IS_VOID
скрывается макрос:
#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID)
Следующая проверка (2)
– имеет ли значение младшие байты, которые укажут на тип объекта.
Если он в добавок является вызываемым (3)
, либо его свойство (поле) [[Class]]
помечает его как функцию (4)
, то это функция.
В противном случае – это объект. Вот так и получаем, что null
– это объект.
Последующие проверки выполняются для number, string и boolean.
Но нет проверки на null
, которая могла бы быть подобной той, что есть для undefined
.
Это может показаться очевиднейшей ошибкой, но не забывайте, что было очень мало времени, чтобы закончить первую версию JavaScript.
Можете сами ознакомиться с ранней версией JS.