Два способа сказать «ничего»
В большинстве языков для обозначения «пустоты» есть одно значение. В JavaScript их целых два: null и undefined. Они ведут себя достаточно похоже, чтобы запутать новичков, и достаточно по-разному, чтобы подставить опытных разработчиков. Разобраться в этой разнице стоит — минут десять вашего времени это точно окупит.
Если коротко:
undefined— то, что JavaScript подставляет сам, когда чего-то не хватает.null— то, что вы пишете сами, когда хотите явно сказать: «здесь намеренно пусто».
Обратите внимание на закономерность: каждый undefined выше появился потому, что JavaScript не смог найти значение. А null возник потому, что кто-то явно его написал.
Откуда берётся undefined
undefined всплывает в нескольких конкретных ситуациях, и все они — вариации на тему «значения попросту нет»:
В каждом из этих случаев JavaScript пошёл за значением и ничего не нашёл. undefined — это способ движка сказать: «Я посмотрел, значения там не было».
Технически undefined можно присвоить и самому (let x = undefined;), но так делать не стоит. Пусть undefined останется сигналом «JavaScript ничего не нашёл». А когда решение принимаете вы — используйте null.
Откуда берётся null
null появляется только тогда, когда его кто-то написал руками. В этом и смысл — это осознанный маркер.
DOM API активно использует null: вызов document.getElementById("missing") вернёт именно null, а не undefined. Браузер как бы говорит: «Я искал — такого элемента нет». То же самое с JSON.parse("null") — результат будет null, ведь в JSON вообще нет undefined.
Если коротко: undefined — это отсутствие значения по умолчанию, а null — отсутствие, которое мы выбрали сами.
typeof null: знаменитый баг
А теперь — легендарная странность:
То, что typeof null возвращает "object" — это баг из 1995 года, который нельзя было починить, не сломав кучу уже работающих сайтов, так что его оставили навсегда. null — это не объект, а примитив, как undefined, числа, строки и булевы значения. Просто typeof на этот счёт нагло врёт.
Отсюда практический вывод: для проверки на null оператор typeof бесполезен. Сравнивайте напрямую:
Или, что бывает чаще, проверить оба варианта сразу — об этом в следующем разделе.
Проверка на null и undefined: идиома == null
В большинстве случаев вам не важно, какое именно «пустое» значение пришло — нужно просто понять, есть что-то или нет, прежде чем с этим работать. Стандартный приём — нестрогое сравнение с null:
value == null возвращает true ровно для null и undefined и false для всего остального — включая 0, "" и false, что обычно как раз и нужно. Это тот самый редкий случай, когда == предпочтительнее ===. Линтеры об этом знают и такую проверку не ругают.
Если хочется написать явно — value === null || value === undefined означает ровно то же самое и читается без подвоха.
Операторы для null и undefined: ?? и ?.
В язык специально добавили два оператора, чтобы упростить работу с null и undefined. Оба считают эти два значения взаимозаменяемыми, а всё остальное не трогают.
Оператор нулевого слияния (??) подставляет значение по умолчанию, только если слева стоит null или undefined:
Сравни с ||, который заменит 0 на 3, потому что 0 — falsy-значение. ?? строже: он срабатывает только для двух nullish-значений.
Опциональная цепочка (?.) прерывает вычисление цепочки и возвращает undefined, как только натыкается на null или undefined:
Оба оператора существуют потому, что вопрос «а это значение nullish?» возникает буквально на каждом шагу. Подробнее мы разберём их дальше по курсу.
Значения по умолчанию срабатывают только для undefined
Тонкий, но важный момент: значения параметров по умолчанию подставляются только при undefined, но не при null.
Если передать null, это воспринимается как «я намеренно выбрал отсутствие значения», и функция это уважает. Хотите, чтобы null тоже запускал значение по умолчанию — используйте ?? внутри функции:
Эта разница регулярно сбивает с толку. Значения по умолчанию срабатывают на отсутствующий аргумент, а ?? — на nullish-аргумент (то есть null или undefined).
JSON и пропавший undefined
В JSON есть null, но нет undefined. Из-за этого при сериализации случаются тихие сюрпризы:
Поле age просто исчезло — JSON.stringify выбрасывает свойства со значением undefined. А вот null выживает, потому что JSON его поддерживает. Прогон объекта через JSON туда-обратно — типичный сценарий, в котором свойства с undefined тихо пропадают.
В массивах же undefined превращается в null:
Если вы проектируете payload для API, для полей «значения нет» лучше использовать null, а не undefined. Он переживёт сериализацию.
Когда что использовать: null или undefined
Разумное соглашение для вашего кода:
- Пусть
undefinedозначает «значение не передано» — пропущенные аргументы, необъявленные переменные, отсутствующие свойства. Вручную присваиватьundefinedне нужно. nullиспользуйте, когда хотите явно сказать «тут намеренно пусто» — разлогиненный пользователь, невыбранная опция, очищенное поле формы.- На границах API принимайте оба варианта (через
== nullили??), но сами возвращайте что-то одно, последовательно.
Некоторые стайл-гайды (в том числе у TypeScript) вообще отказываются от null и используют только undefined. Подход вполне рабочий — одно значение проще, чем два. Выберите правило на уровне проекта и придерживайтесь его везде.
Дальше: приведение типов
У null и undefined своё поведение, когда JavaScript приводит их к числу, строке или булеву значению: Number(null) даёт 0, а Number(undefined) — NaN, и из-за такой асимметрии рождаются вполне реальные баги. Следующая тема — приведение типов, и как только вы увидите правила целиком, многие странности JavaScript перестанут казаться магией.
Часто задаваемые вопросы
В чём разница между null и undefined в JavaScript?
undefined означает, что значение ещё не присвоено — именно это JavaScript подсовывает вам для необъявленных переменных, пропущенных аргументов и отсутствующих свойств объекта. А null — это явное «здесь ничего нет», которое вы присваиваете сами и осознанно. Сам по себе JavaScript null никогда не возвращает, его нужно написать руками.
Как одной проверкой отловить и null, и undefined?
Пишите value == null. Нестрогое сравнение считает null и undefined равными друг другу, но не равными ничему другому, поэтому x == null истинно ровно для этих двух значений. Это тот редкий случай, когда == вместо === — идиоматично и безопасно.
Почему typeof null возвращает 'object'?
Это баг из самой первой версии JavaScript, который уже не починить — иначе половина веба сломается. typeof null возвращает 'object', хотя null — это примитив. Если нужно проверить именно на null, сравнивайте напрямую: value === null.
Что использовать в своём коде — null или undefined?
Пусть undefined означает «значение не передали», а null используйте тогда, когда хотите явно сказать «здесь осознанно пусто». Во многих проектах (и в style guide TypeScript) от null отказываются вовсе и обходятся одним undefined. Главное — выберите одну конвенцию на проект и придерживайтесь её.