Перейти к содержимому

Почему null в js объект

  • автор:

Почему typeof null === ‘object’?

Все JavaScript-программисты давно привыкли к тому, что typeof null === ‘object’; // true , хотя фактически null — примитивное значение. Многие знают, что это баг, и лично Брэндан Айк это признает. Этот баг, вероятно, никогда не будет исправлен из-за необходимости сохранения обратной совместимости существующего кода с новыми версиями языка.

Интересна история того, как же это получилось. Она восходит корнями к первой версии языка, а именно — к тому факту, что значения переменных хранились в 32-битных ячейках в следующем формате:
29-31 бит: само значение;
1-3 бита: метка типа данных;

Было всего пять вариантов метки типа:
000: object;
1: integer;
010: double;
100: string;
110: boolean;

Соответственно, если младший бит был равен единице, то оставшийся 31 бит интерпретировался как integer. Если 0 — то тип определялся в зависимости от значения следующих двух бит.

Также было два специальных зарезервированных значения:

undefined (JSVAL_VOID) — целое –2 30
null (JSVAL_NULL) — указатель на NULL (machine code NULL pointer), то есть, метка объекта и ссылка на то, что его численное представление равно нулю.

Так и вышло, что typeof стал определять null как object — он проверял метку типа, которая сообщала ему, что null — это не что иное, как object.

Значение null для объектов. Как это выглядит?

Про undefined: я думаю для того, чтобы значение объекта было undefined, надо просто в нем ничего не писать.

let object = < //ничего здесь нет >

Про null: ну а про null не понятно. Мы же не можем сделать вот так:

let object = < null >//--> Синтаксическая ошибка 

Отслеживать
задан 10 апр 2021 в 20:59
1,869 8 8 серебряных знаков 21 21 бронзовый знак
let object = null;
10 апр 2021 в 21:08

Вы путаете объект и переменную. «Если переменная, представляющая объект, будет иметь значение null». Переменная иметь значение.

– user347472
10 апр 2021 в 21:11

@Алексей Шиманский Так это ж просто обычная переменная, которой вы присвоили значение null . Объект вроде как должен создаваться либо с помощью фигурных скобок, либо с помощью new Object()

10 апр 2021 в 21:38

«Если переменная, представляющая объект, будет иметь значение null или undefined цикл не выполнится ни разу.» => Если есть какая-нибудь переменная, которая предназначена для хранения объекта, но в данный момент она хранит не объект, а значение null или undefined , то цикл не выполнится ни разу.

Исследование бездны null и undefined в JavaScript

Шапка

Говоря о примитивных типах данных в JavaScript, большинство имеет в виду самые основные из них: String, Number, и Boolean. Эти примитивы достаточно предсказуемы, и работают так, как от них и ожидается. Однако, речь в данной статье пойдет об менее обыденных примитивных типах, Null и Undefined, о том, в чём они схожи, различны, и, вообще говоря, необычны.

Понимание null и undefined

В JavaScript null — это литерал и ключевое слово языка, которое представляет собой отсутствие какого-либо объекта. Другими словами, null указывает «в никуда». В свою очередь, хоть и похожий по смыслу undefined , олицетворяет отсутствие значения как такового. Оба абсолютно неизменны, не имеют свойств и методов и не способны их иметь. Фактически, попытка обратиться к какому-нибудь свойству, или присвоить его, приведёт к ошибке TypeError . Оба этих примитива, как намекают их имена, совершенно лишены значений.

Это самое отсутствие значения приводит к тому, что они считаются ложными, в том смысле, что они приводятся к false если используются в качестве условия, например, в конструкции if . А если сравнить null и undefined с другими ложными значениями при помощи оператора нестрогого сравнения ( == ), то окажется, что они не равны ничему, кроме самих себя:

null == 0; // false undefined == ""; // false null == false; // false undefined == false; // false null == undefined; // true 

Несмотря на эти сходства, null и undefined не эквивалентны. Каждый из них является представителем своего типа: undefined — представляет тип Undefined, а null , соответственно — тип Null. Это легко доказать, сравнив их при помощи оператора строгого сравнения ( === ), который принимает в расчёт не только значения, но и типы данных:

undefined === null; // false 

Это важное различие, и оно не случайно, ведь эти примитивы служат для разных целей. Чтобы их различать, вы можете считать undefined неожиданным отсутствием значения, а null — умышленным отсутствием значения.

Получение undefined

Есть множество способов получить значение undefined в коде. Обычно это происходит при попытке получить значение там, где значения нет. В этом случае JavaScript, будучи динамическим, слабо типизированным языком, не покажет ошибку, а выдаст значение по умолчанию, undefined .

Любая объявленная переменная, которой при создании не присвоено никакого значения, имеет значение undefined :

var foo; // по умолчанию undefined 

Значение undefined также получается при попытке обратиться к несуществующему свойству объекту или элементу массива:

var array = [1, 2, 3]; var foo = array.foo; // свойство foo не существует, возвращается undefined var item = array[5]; // в массиве нет элемента 5, возвращается undefined 

Если в функции нет оператора return , она возвращает undefined :

var value = (function( )<>)(); // возвращает undefined 

Если функции не был передан какой-либо аргумент, он становится undefined :

(function(undefined)< // параметр равен undefined >)(); 

Помимо всего вышеперечисленного, для получения undefined может использоваться оператор void . Некоторые библиотеки, вроде Underscore пользуются этим для надежной проверки типов, потому как void нельзя переопределить, и он всегда возвращает undefined :

function isUndefined(obj)< return obj === void 0; > 

Наконец, undefined — это предопределённая глобальная переменная (а не ключевое слово, как null ), которая равна undefined :

'undefined' in window; // true 

Начиная с пятой версии ECMAScript эта переменная доступна только для чтения, а, вот, в предыдущих версиях её было возможно переопределить.

Применение null

В первую очередь null отличается своим применением, и в отличие от undefined , null больше используется для присваивания значения. Как раз из-за этого оператор typeof для null возвращает «object». Изначально это объяснялось тем, что null использовался (и используется) как пустая ссылка там, где ожидается объект, что-то вроде заглушки. Такое поведение typeof было позже признано багом, и, хотя было предложено это поведение исправить, пока что, в целях обратной совместимости, всё остается как есть.

Вот, почему окружение JavaScript не выставляет никаких значений в null , и это делается только программно. В документации на MDN написано следующее:

В различных API null часто возвращается в тех местах, где ожидается объект, но такой объект подобрать нельзя.

Это правдиво для DOM, который не зависит от языка и никак не описывается в документации ECMAScript. Из-за того, что используется внешний API, попытка получить отсутствующий элемент возвращает null , а не undefined .

Вообще, если нужно присвоить «не-значение» переменной или свойству, передать его в функцию, или вернуть из функции, то null — это почти всегда лучший вариант. Упрощённо: JavaScript использует undefined , а программисты должны использовать null .

Другой способ применения null — явное «зануливание» переменной ( object = null ), когда ссылка на объект больше не требуется. Кстати, это считается хорошей практикой. Присваивая null , вы фактически удаляете ссылку на объект, и если на него нет других ссылок, он отправляется к сборщику мусора, таким образом возвращая доступную память.

Копнём глубже

Причина того, что null и undefined эдакие чёрные дыры, кроется не только в их поведении, но ещё и в том, как они обрабатываются внутри окружения JavaScript. Они не обладают теми характеристиками, которые обычно присущи другим примитивам и встроенным объектам.

Начиная с ES5 метод Object.prototype.toString , ставший стандартом де-факто для проверки типов, стал полезен в этом отношении и для null с undefined :

Object.prototype.toString.call(null); // [object Null] Object.prototype.toString.call(undefined); // [object Undefined] 

Однако, на самом деле у null и undefined этот метод не возвращает внутреннее свойство [[Class]] . По документации он работает следующим образом:

  1. Если значение this равно undefined , вернуть «[object Undefined]» .
  2. Если значение this равно null , вернуть «[object Null]» .
  3. Пусть O равно результату вызова ToObject с this , переданным как аргумент.
  4. Пусть class равно внутреннему свойству [[Class]] объекта O.
  5. Вернуть значение String, которое является результатом сложения трёх строк «[object » , class, и «]» .

Этот метод просто возвращает заготовленную строку, если обнаруживает null или undefined , просто чтобы унифицировать функциональность с другими объектами. Такое поведение встречается сплошь и рядом во всей документации, большая часть методов содержат простую проверку, и если встретился null или undefined , возвращают значение сразу. Фактически, нигде не написано, что у них содержатся какие-либо внутренние свойства, обычно имеющиеся у каждого нативного объекта. Это как если бы они вообще не были объектами. Интересно, эти примитивы в окружении JavaScript как-то явно и особо обрабатываются? Может быть, кто-то более знакомый с имплементацией мог бы подсказать.

Заключение

Неважно, насколько необычными кажутся примитивы null и undefined , понимание разницы между ними и их различиями в использовании согласно JavaScript очень важно для понимания языка в целом. Это понимание, конечно же, само по себе не заставит работать ваше приложение или, например, не сломает его, но, строго говоря, оно положительно скажется в долгосрочной перспективе, облегчив вам разработку и отладку будущих проектов.

Комментарий переводчика

При написании этой статьи автор забыл упомянуть одну важную деталь: у примитивов, как таковых, не может быть свойств вообще, они есть только у объектов. А при попытке получить свойство у примитива, он будет неявно преобразован в объект. В этом легко убедиться:

var s = 'test', o = Object(s); o.foo = 42; s.foo = 42; o.foo; // 42 s.foo; // undefined 

Дело в том, что null и undefined просто нельзя преобразовать в объект, на чем и строится объяснение ключевых особенностей этих примитивов автором этой статьи.

Также, фраза про то, что null в окружении JavaScript без явного присваивания не используется, неверна. В конце цепочки прототипов находится null , и это как раз тот случай, когда ожидается объект, но его нет:

Object.getPrototypeOf(Object.prototype); // null 

Аватар

Оригинальная статья: Exploring the Abyss of Null and Undefined in JavaScript Статью вычитывали: SilentImp, cosmiksoul, FMRobot, subzey

© 2013 Frontender Magazine

Кроме материалов, опубликованных под лицензией Creative Commons

Евгений Степанищев

Пишу, по большей части, про историю, свою жизнь и немного про программирование. Живу в Казани.

Почему у null в JS тип «object»?

На «Хабре» вышла статья, проливающая свет на загадку — почему же у null в Джаваскрипте тип «объект». Причём, если попытаться этому «объекту» добавить какое-то свойство, то браузер воспротивится: скажет, что null объектом не является.

Null как объект (13.48КиБ)

Вообще, я этому особого значения не придавал. Ну да, примитивное значение, имеет тип объект, хотя по факту им не является, смысла я в этом не вижу, это мешает, но поменять это нельзя.

Оказывается, это баг. Точнее даже не баг, а просто «так получилось». В ранних интерпретаторах Джаваскрипта тип хранился в первых трёх битах значения. Для типа «а это у нас null» значения не хватило, ему дали тип «объект», а под значение выделили нулевой указатель. Ну typeof, особо не заморачиваясь, выдавала object.

К сожалению, программисты поленились сделать дополнительную проверку. Можно было при наличии нулевого указателя попросить typeof выводить «null» и дело в шляпе. Сейчас, как оказалось, куча кода завязано на это поведение и ломать уже ничего никто не будет.

9 комментариев

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

История в тему: недавно на собеседовании вьюноши учудили и начали спрашивать меня про типы данных. Тогда про null для них и случилось открытие ) Детишки про фишку тупо не знали. Ясно, что на работу уже не позвали. А снаружи это вполне солидный ресурс.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для shaltai-boltai:

Честно сказать, если его расширить, то мне польза неочевидна как-то 🙂 Давать ему методы через прототип? 🙂 Разве что только для целостности языка какая-то польза будет.

А вот проблемы некоторые это рождает. Например, известнейшая проверка «а объект ли это» снабжается костылём «и не null».

Ну да, больше для целостности. Null-объект можно делать типизированным, можно какую-то историю для undo хранить. Всё лучше, чем ничего.

Ну вообще-то, NULL теоретически подпадает под возможное значение объекта.
Во многих языках с проверкой типа любой объект может быть NULL.
Я думал из этих побуждений и здесь так сделано, а не баг.
Но, конечно на практике в JS это не удобно.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *