Программы
История typeof null в JavaScript – ошибка, необходимая для обратной совместимости

История typeof null в JavaScript – ошибка, необходимая для обратной совместимости

В JavaScript есть множество исключений и просто забавных подходов к преобразованию типов. Про "один из них" и хочется рассказать.

Перевод статьи "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.

Также может быть вам интересно:

Почему Python - не язык программирования будущего

Даже если он будет пользоваться большим спросом еще несколько лет...

Читать »

Антипаттерн "Заочный менеджер". Ниндзя офисного мира

Менеджер, который не мешает работать... ну не идеал ли? Другое дело, если он и не помогает - зачем он нужен?

Читать »
Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Настройка журналирования (логирования) в Python с примерами

Настройка журналирования (логирования) в Python с примерами

Во время работы программы часто нужно сохранять некоторые важные записи о процессе выполнения команды. В Python есть довольно мощный модуль для работы с логами - давайте разберёмся с тем, как его использовать.

Фото Шаблон разработки ПО — Model View Controller (MVC)

Шаблон разработки ПО — Model View Controller (MVC)

MVC - один из самых распространённых архитектурных шаблонов разработки. Часто используется в различных фреймворках. В том числе и в Django.

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.

Фото Пример своей консольной команды в Django проекте

Пример своей консольной команды в Django проекте

Если вы работали с Django проектом, то, скорее всего, запускали команды из консоли (manage.py). В Django есть простой способ писать свои команды для управления проектом.

Фото Разграничение прав доступа на Django сайте

Разграничение прав доступа на Django сайте

Почти на любом веб-сайте необходимо разделять пользователей на группы и предоставлять им разные возможности. В Django есть довольно серьёзная система прав доступа для пользователей - давайте её рассмотрим!

Фото Пользователи и их создание в Django - своя регистрация на сайте

Пользователи и их создание в Django - своя регистрация на сайте

Если вашим сайтом должны активно пользоваться несколько человек, то полезно их различать, а значит - надо уметь создавать пользователей, либо предоставлять возможность регистрации Django пользователей.