И кажется, что будет всегда.
Как вы знаете, все значения в JavaScript являются либо примитивами, либо объектами.
Существует 7 примитивных типов данных:
-нить
-число
-большой
-логическое значение
-неопределенный
-символ
-нулевой
Проверим их типы с помощью оператора typeof:
console.log(typeof "some text"); // string console.log(typeof 1234); // number console.log(typeof 9007199254740991n); // bigint console.log(typeof true); // boolean console.log(typeof undefined); // undefined console.log(typeof Symbol('some text')); // symbol console.log(typeof null); // object
Ждать! Что?
Почему typeof null
является объектом?
Это ошибка, которую нельзя решить, поскольку она может привести к поломке текущего кода.
Давайте погрузимся глубоко.
В чем разница между объектами и примитивами:
Примитивы неизменяемы, к ним нельзя добавлять свойства:
let myString = "abc"; // try to add property "figure" myString.figure = 4; console.log(myString.figure) // undefined
А примитивы сравниваются по значению, они считаются равными, если имеют одинаковое содержимое:
console.log("abc" === "abc") // true
Объекты. Все не-примитивные значения являются объектами. Объекты изменчивы:
let myObject = {}; // try to add property "figure" myObject.figure = 123; console.log(myObject.figure); // 123
А объекты сравниваются по ссылке. Каждый объект имеет свою собственную идентичность, и два объекта считаются равными только в том случае, если они на самом деле являются одним и тем же объектом:
console.log({} === {}) // false let myObject = {}; console.log(myObject === myObject) // true
Типы объектов-оболочек. Типы-примитивы boolean, number и string имеют соответствующие типы объектов-оболочек Boolean, Number и String. Экземпляры последних являются объектами и отличаются от примитивов, которые они обертывают:
console.log(typeof new String("abc") ) // object console.log(typeof abc) // string console.log(new String("abc") === "abc") // false
typeof — это оператор, который классифицирует примитивы и помогает отличать их от объектов
Проблема «typeof null» унаследована от оригинальной версии JavaScript.
В этой версии значения хранились в 32-битных блоках, которые включали тег короткого типа (1–3 бита) и фактические данные значения. Теги типа были сохранены в младших битах единиц.
теги типа (1–3 бита) + значение (29–31 бит) = всего 32 бита
Тегов было пять типов:
- 000: объект. Данные являются ссылкой на объект.
- 1: внутр. Данные представляют собой 31-битное целое число со знаком.
- 010: двойной. Данные являются ссылкой на двойное число с плавающей запятой.
- 100: строка. Данные являются ссылкой на строку.
- 110: логическое значение. Данные являются логическими.
Есть 2 специальных значения:
- undefined (JSVAL_VOID) — целое число −2³⁰ (число вне диапазона целых чисел).
- null (JSVAL_NULL) был указателем NULL в машинном коде. Или: тег типа объекта плюс нулевая ссылка.
Поскольку он начинается с 000 (это тег типа объекта), тип считает, что это объект.
Код движка для 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; }
Он начинается с проверки того, является ли значение v неопределенным (VOID) в операторе:
если (JSVAL_IS_VOID(v))
Затем он проверяет, является ли значение объектом. Проверка наличия тега типа, начинающегося с 000.
Если это не объект, он проверяет, является ли это числом, строкой или логическим значением.
Нет никакой проверки на NULL.
Это ошибка, которую легко увидеть, но поскольку она не была исправлена вовремя, исправить ее впоследствии стало намного сложнее.
Даже создатель JavaScript Брендан Эйх прокомментировал в блоге эту тему, заявив, что это действительно ошибка.
Брендан Эйх однажды подумал о необходимости исправления typeof, но отклонил это предложение, заявив: «Я думаю, что уже слишком поздно исправлять typeof. Изменение, предложенное для typeof null, нарушит существующий код».
В другом обсуждении он написал: «В общем, typeof кажется беспорядком, который будет трудно разумно реформировать. У нас есть основания полагать, что typeof null === «object» *является ошибкой*, которая может укусить реальный контент из нашего поиска в Интернете. Возможно, было бы лучше оставить typeof в покое и объявить его устаревшим, *но я по-прежнему поддерживаю нулевое исправление ошибок*».
Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Получите эксклюзивный доступ к возможностям написания и советам в нашем сообществе Discord.