Исследование бездны null и undefined в JavaScript
Говоря о примитивных типах данных в JavaScript, большинство имеет в виду самые основные из них: String, Number, и Boolean. Эти примитивы достаточно предсказуемы, и работают так, как от них и ожидается. Однако, речь в данной статье пойдет об менее обыденных примитивных типах, Null и Undefined, о том, в чём они схожи, различны, и, вообще говоря, необычны.
Понимание null и undefined
В JavaScript null — это литерал и ключевое слово языка, которое представляет собой отсутствие какого-либо объекта. Другими словами, null указывает «в никуда». В свою очередь, хоть и похожий по смыслу undefined , олицетворяет отсутствие значения как такового. Оба абсолютно неизменны, не имеют свойств и методов и не способны их иметь. Фактически, попытка обратиться к какому-нибудь свойству, или присвоить его, приведёт к ошибке TypeError . Оба этих примитива, как намекают их имена, совершенно лишены значений.
Это самое отсутствие значения приводит к тому, что они считаются ложными, в том смысле, что они приводятся к false если используются в качестве условия, например, в конструкции if . А если сравнить null и undefined с другими ложными значениями при помощи оператора нестрогого сравнения ( == ), то окажется, что они не равны ничему, кроме самих себя:
Несмотря на эти сходства, null и undefined не эквивалентны. Каждый из них является представителем своего типа: undefined — представляет тип Undefined, а null , соответственно — тип Null. Это легко доказать, сравнив их при помощи оператора строгого сравнения ( === ), который принимает в расчёт не только значения, но и типы данных:
Это важное различие, и оно не случайно, ведь эти примитивы служат для разных целей. Чтобы их различать, вы можете считать undefined неожиданным отсутствием значения, а null — умышленным отсутствием значения.
Получение undefined
Есть множество способов получить значение undefined в коде. Обычно это происходит при попытке получить значение там, где значения нет. В этом случае JavaScript, будучи динамическим, слабо типизированным языком, не покажет ошибку, а выдаст значение по умолчанию, undefined .
Любая объявленная переменная, которой при создании не присвоено никакого значения, имеет значение undefined :
Значение undefined также получается при попытке обратиться к несуществующему свойству объекту или элементу массива:
Если в функции нет оператора return , она возвращает undefined :
Если функции не был передан какой-либо аргумент, он становится undefined :
Помимо всего вышеперечисленного, для получения undefined может использоваться оператор void . Некоторые библиотеки, вроде Underscore пользуются этим для надежной проверки типов, потому как void нельзя переопределить, и он всегда возвращает undefined :
Наконец, undefined — это предопределённая глобальная переменная (а не ключевое слово, как null ), которая равна undefined :
Начиная с пятой версии 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 :
Однако, на самом деле у null и undefined этот метод не возвращает внутреннее свойство [[Class]] . По документации он работает следующим образом:
- Если значение this равно undefined , вернуть «[object Undefined]» .
- Если значение this равно null , вернуть «[object Null]» .
- Пусть O равно результату вызова ToObject с this , переданным как аргумент.
- Пусть class равно внутреннему свойству [[Class]] объекта O.
- Вернуть значение String, которое является результатом сложения трёх строк «[object » , class, и «]» .
Этот метод просто возвращает заготовленную строку, если обнаруживает null или undefined , просто чтобы унифицировать функциональность с другими объектами. Такое поведение встречается сплошь и рядом во всей документации, большая часть методов содержат простую проверку, и если встретился null или undefined , возвращают значение сразу. Фактически, нигде не написано, что у них содержатся какие-либо внутренние свойства, обычно имеющиеся у каждого нативного объекта. Это как если бы они вообще не были объектами. Интересно, эти примитивы в окружении JavaScript как-то явно и особо обрабатываются? Может быть, кто-то более знакомый с имплементацией мог бы подсказать.
Заключение
Неважно, насколько необычными кажутся примитивы null и undefined , понимание разницы между ними и их различиями в использовании согласно JavaScript очень важно для понимания языка в целом. Это понимание, конечно же, само по себе не заставит работать ваше приложение или, например, не сломает его, но, строго говоря, оно положительно скажется в долгосрочной перспективе, облегчив вам разработку и отладку будущих проектов.
Комментарий переводчика
При написании этой статьи автор забыл упомянуть одну важную деталь: у примитивов, как таковых, не может быть свойств вообще, они есть только у объектов. А при попытке получить свойство у примитива, он будет неявно преобразован в объект. В этом легко убедиться:
Дело в том, что null и undefined просто нельзя преобразовать в объект, на чем и строится объяснение ключевых особенностей этих примитивов автором этой статьи.
Также, фраза про то, что null в окружении JavaScript без явного присваивания не используется, неверна. В конце цепочки прототипов находится null , и это как раз тот случай, когда ожидается объект, но его нет:
Оригинальная статья: Exploring the Abyss of Null and Undefined in JavaScript Статью вычитывали: SilentImp, cosmiksoul, FMRobot, subzey
Типы данных JavaScript
Осваивайте профессию, начните зарабатывать, а платите через год!

Курсы Python Акция! Бесплатно!

Станьте хакером на Python за 3 дня

Веб-вёрстка. CSS, HTML и JavaScript


Курс Bootstrap 4

Станьте веб-разработчиком с нуля

В этом уроке мы познакомимся с очень важным понятием – типы данных JavaScript. Мы рекомендуем внимательно отнестись к данному понятию – если не осознать его с самого начала, то потом придется часто сталкиваться со странным поведением созданной вами программы.
Динамическая типизация
В процессе работы компьютерные программы манипулируют различными значениями, каждое из которых может быть обработано в языке программирования и относится к определённому типу данных.
В JavaScript типы данных можно разделить на две категории: простые (их также называют примитивными) типы и составные (их также называют ссылочными или объекты).
JavaScript – это слабо типизированный или динамический язык программирования, который позволяет определять типы данных, осуществлять синтаксический анализ и компиляцию как говорится «на лету», на этапе выполнения программы. Это значит, что вам не нужно определять тип переменной заранее. Тип определится автоматически во время выполнения программы.
Таким образом, в различных участках программы одна и та же переменная может принимать значения разных типов:
Типы данных
Стандарт ECMAScript® определяет следующие типы данных:
- Простые (их также называют примитивными) типы:
- логический (англ. Boolean) — может принимать два возможных значения, иногда называемых истиной (true) и ложью (false);
- нулевой (англ. Null) – значение null представляет ссылку, которая указывает, обычно намеренно, на несуществующий или некорректный объект или адрес;
- неопределённый (англ. Undefined) – обозначает предопределенную глобальную переменную, инициализированную неопределенным значением;
- числовой (англ. Number) – числовой тип данных в формате 64-битного числа двойной точности с плавающей запятой;
- строковый (англ. String) – представляет собой последовательность символов, используемых для представления текста;
- символ (англ. Symbol) — тип данных, экземпляры которого уникальны и неизменяемы. (новый в ECMAScript 6).

Разница между примитивными и составными типами
Прежде чем рассмотреть каждый тип данных, познакомимся сначала с оператором typeof . Оператор typeof возвращает строку, описывающую тип данных переменной.
Продемонстрируем его работу на следующем примере:Сценарий в приведенном примере объявляет переменные, выполняет их инициализацию (присваивает значения), а затем выводит тип каждой переменной.
Разница между примитивными и составными типами данных проявляется при копировании их значений.
Когда переменной присваивается значение простого типа, то в переменную записывается само значение (например число). Когда мы присваиваем переменную простого типа в другую, она копирует значение. В результате каждая переменная будет иметь своё значение и изменения в одной из переменных никак не сказывается на значении другой переменной:
Когда переменной присваиваем значение составного (ссылочного) типа, то в переменную записывается ссылка на значение (ссылка на объект). Когда мы присваиваем одну переменную (в значении которой ссылка на составное значение) другой переменной, то происходит копирование ссылки на составное значение. В результате обе переменные ссылаются на одно и то же составное значение и изменения в значении одной из переменных будут сказываться на другой переменной:
Примитивные типы
Все типы данных в JavaScript, кроме объектов, являются неизменяемыми или иммутабельными (англ. immutable). Это значит, что их значения не могут быть модифицированы, а только перезаписаны новым другим значением. Например, строки нельзя корректировать посимвольно – их можно переписать только полностью. Значения таких типов называются «примитивными».
Простейшие данные, с которыми может оперировать программа, называются литералами. Литералы — это числа или строки, которые используются для представления значений в JavaScript. Предоставляемая информация может быть самой разнообразной, а поэтому значения бывают различных типов. Простейшие типы данных в JavaScript называются основными типами данных: числа, строки и логические значения. Все они относятся к «примитивным».
Булевый (логический) тип «boolean»
Логические, или булевы значения (по фамилии их изобретателя — Буля), могут иметь лишь одно из двух значений: true (истина) или false (ложь). Значения true или false обычно появляются в операциях сравнения или логических операциях.
Приведенная ниже программа создает булеву переменную, а затем тестирует ее значение с использованием инструкции if/else :
В качестве сравнительного выражения можно использовать любое выражение. Любое выражение, которое возвращает значение 0, null, undefined или пустую строку, интерпретируется как false . Выражение, определяющее какое-либо другое значение, интерпретируется как true .
Примечание: При записи булевых значений их не заключают в кавычки: var myVar = true;
В то же время объявление var myVar = «true» создает строковую переменную.Тип данных Number
В JavaScript, нет различия между целым числом и числом с плавающей точкой – по сути, JavaScript представляет все числа в качестве значения с плавающей точкой.
Для представления чисел в JavaScript используется 64-битный формат, определяемый стандартом IEEE-754. Этот формат способен представлять числа в диапазоне от ±1,7976931348623157 × 10 308 до ±5 × 10 -324 .
Число, находящееся непосредственно в коде программы, называется числовым литералом. Помимо десятичных целых литералов JavaScript распознает шестнадцатеричные значения.
Числа в шестнадцатиричном формате могут включать любую последовательность цифр от 0 до 9 и буквы от a до f, которая обязательно начинается с последовательности символов «0x».Кроме того, JavaScript содержит специальные числовые значения:
- NaN (не число или ошибка вычислений). Является результатом некорректной математической операции над недопустимыми данными, такими как строки или неопределенное значение.
- Infinity (положительная бесконечность). Используется, если положительное число слишком велико и не может быть представлено в JavaScript.
- -Infinity (отрицательная бесконечность). Используется, если отрицательное число слишком велико и не может быть представлено в JavaScript.
- ±0 (положительный и отрицательный 0). JavaScript различает положительный и отрицательный ноль.
Тип данных String
Строковый тип (string) — это неизменяемая, упорядоченная последовательность 16-битных значений, каждое из которых представляет символ Unicode (буквы, цифры, знаки пунктуации, специальные символы и пробелы). Строки могут быть пустыми либо состоять из одного и более символов. Строки создаются при помощи двойных («) или одинарных (‘) кавычек. В строке, ограниченной парой одинарных кавычек, можно использовать двойные кавычки, и наоборот – одинарные кавычки можно использовать в строке, заключенной в пару двойных кавычек:
Настоящее и будущее безопасной работы с null и undefined в JavaScript
Автору материала, перевод которого мы сегодня публикуем, недавно попался один вопрос на StackOverflow, который заставил его задуматься об обработке значений null и undefined в JavaScript. Здесь он приводит анализ текущей ситуации, показывает некоторые приёмы безопасной работы с null и undefined , а также, говоря о будущем, рассматривает оператор ?. .
Основные сведения
Undefined — это примитивное значение, которое автоматически назначается объявленным, но неинициализированным переменным. Это значение можно получить, обратившись к несуществующему свойству объекта или к несуществующему аргументу функции.
Null — это ещё одно примитивное значение, которое представляет собой отсутствие значения. Например, если переменной присвоено значение null , это можно трактовать как то, что на данном этапе работы программы эта переменная существует, но у неё пока нет ни типа, ни значения.
Посмотрим, что произойдёт, если выполнить такой код:
Он выдаст следующую ошибку:
Похожее можно видеть и при попытках работы с переменными, имеющими значение null .
Проверка на null и undefined
Как избежать подобных явлений? К счастью для нас, JavaScript поддерживает вычисление логических выражений по сокращённой схеме (short-circuit evaluation). Это означает, что для того, чтобы избежать вышеописанной ошибки TypeError , можно написать следующий код:
Но что если нужно пойти глубже, например, добраться до чего-то вроде obj.prop1.prop2.prop3 ? В подобной ситуации можно попытаться решить задачу, выполнив множество проверок:
Хотя это и работает, смотрятся такие конструкции не очень-то хорошо.
А что если нам нужно выводить некое стандартное значение, если в подобной цепочке встречается undefined или null ? Это возможно, но тогда кода придётся писать ещё больше:
Прежде чем продолжить размышления о null и undefined в JavaScript, поговорим о том, как подобные значения обрабатываются в других языках.
Другие языки
Надо отметить, что проблема работы с неопределёнными значениями присутствует в большинстве языков программирования. Посмотрим, как проводятся проверки, подобные вышеописанным, в других языках.
В Java есть api Optional :
▍Kotlin
В Kotlin (он, как и Java, использует JVM) существуют операторы elvis ( ?: ) и safe-call ( ?. ).
И, наконец, в C# есть операторы null-condition ( ?. ), и null-coalescing ( ?? ).
Как работать с undefined в JS?
После рассмотрения возможностей по работе с неопределёнными значениями в других языках, я задался вопросом о том, есть ли возможность безопасно работать с undefined в JavaScript и при этом не писать километры кода. Поэтому я приступил к экспериментам с регулярными выражениями. Мне хотелось создать функцию, которая позволила бы безопасно получать доступ к свойствам объектов.
После того, как я создал эту функцию, я узнал о методе lodash._get , который имеет такую же сигнатуру:
Однако, честно говоря, я не отношусь к фанатам строковых путей, поэтому я начал искать способ, который позволил бы избежать их использования. В результате я создал решение, основанное на прокси-объектах:
Будущее безопасной работы с null и undefined в JavaScript
Сейчас в комитете TC39 имеется предложение, которое позволяет пользоваться следующими конструкциями:
По мне, так выглядит это очень аккуратно. Однако это предложение пока находится на достаточно ранней стадии согласования, то есть, его потенциальное появление в языке может занять некоторое время. Но, несмотря на это, существует плагин для babel, который позволяет уже сегодня пользоваться подобными конструкциями.
Итоги
Значения вроде null и undefined присутствуют в программировании уже очень давно, и ничто не указывает на то, что они в скором времени исчезнут. Вероятно, концепция значения null является самой нелюбимой в программировании, однако у нас есть средства для обеспечения безопасной работы с подобными значениями. В том, что касается работы с null и undefined в JavaScript, можно воспользоваться несколькими подходами рассмотренными выше. В частности, вот код функций, предложенных автором этого материала. Если вас заинтересовал оператор ?. , появление которого ожидается в JS, взгляните на одну из наших предыдущих публикаций, в которой затрагивается этот вопрос.
Уважаемые читатели! Какой из упомянутых в этом материале способов безопасной работы с null и undefined в JavaScript нравится вам больше всего?
Null undefined что выведет
Значение null представляет отсутствие какого-либо объектного значения. В JavaScript, null является примитивом, и в контексте логических операций, рассматривается как ложное (falsy).
Интерактивный пример
Синтаксис
Описание
Значение null записывается литералом null . Оно является самостоятельным, а не свойством глобального объекта (как undefined ). В API, null часто присутствует в местах где ожидается объект, но подходящего объекта нет.
Отличия между null и undefined
null является определённым значением отсутствия объекта, тогда как undefined обозначает неопределённость. Например:
При проверке на null или undefined , помните о различии между операторами равенства (==) и идентичности (===): с первым, выполняется преобразование типов.