Menu

Оператор ?? в JavaScript: значения по умолчанию

Как оператор ?? подставляет значение по умолчанию только когда слева null или undefined — и почему он удобнее || в большинстве реальных задач.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Умное значение по умолчанию

В JavaScript издавна есть короткий способ задать дефолтное значение: value || fallback. В целом работает, но с подвохом — любое falsy-значение воспринимается как «пустое». 0, '' и false тоже запускают подстановку запасного варианта, даже если именно они и есть настоящий ответ.

Оператор nullish coalescing ?? решает эту проблему. Он подставляет запасное значение только тогда, когда слева null или undefined:

0 и '' остаются. А вот null и undefined — нет. Вот и весь оператор.

Почему || не спасает

Вот тот самый классический баг, ради которого и придумали ??:

Пользователь явно указал громкость 0 (тишина) и пустой ник. Оба значения тихо затёрлись, потому что || не умеет отличать «ничего не задано» от просто falsy-значения.

Меняем на ??:

Теперь значения 0 и '' проходят без изменений, а дефолт подставляется только для реально отсутствующих полей. В 99% случаев именно это вам и нужно.

Как это работает в голове

У оператора ?? ровно один вопрос: слева null или undefined? Всё остальное его не волнует.

Значение слеваleft || right вернётleft ?? right вернёт
nullrightright
undefinedrightright
0rightleft (0)
''rightleft ('')
falserightleft (false)
NaNrightleft (NaN)
любой объектleftleft

Берите ??, когда в ваших данных falsy-значения имеют смысл. Берите ||, когда вам правда нужно отсечь всё falsy — пустые строки, нули и так далее. Такие случаи бывают, но гораздо реже, чем принято думать.

Короткое замыкание

Как и || с &&, оператор ?? работает через короткое замыкание. Если слева не nullish — правая часть вообще не выполняется:

expensiveDefault() вызовется только один раз — для b. Это удобно, когда в качестве запасного значения у вас идёт вызов функции, сетевой запрос или любая другая операция, которую лишний раз запускать не хочется.

Связка с опциональной цепочкой

Операторы ?? и ?. появились вместе в ES2020 и изначально задумывались как команда. Опциональная цепочка (?.) проходит по пути, в котором какое-то звено может отсутствовать, и возвращает undefined, если что-то не нашлось. А ?? затем подставляет разумное значение по умолчанию:

Без этой парочки тот же код превращается в уродливую цепочку проверок через && или в try/catch. А с ними безопасный доступ к свойствам и разумные значения по умолчанию укладываются в одну строку.

Оператор ??=: присваивание при null или undefined

a ??= b присваивает b переменной a только тогда, когда a равно null или undefined. Это и есть оператор логического nullish-присваивания, и он работает с коротким замыканием — если у a уже есть значение, правая часть попросту не вычисляется.

Обрати внимание: verbose: false и retries: 0 остались нетронутыми — ??= подставляет значение только туда, где его реально нет. Для сравнения: ||= затёр бы оба этих поля.

Приоритет операторов и правило скобок

Оператор ?? намеренно отказывается сочетаться с || или && без скобок. Такой код выбросит SyntaxError:

const x = a || b ?? c;   // SyntaxError

Разработчики языка посчитали, что приоритет операторов здесь слишком неоднозначный, и решили заставить нас явно указывать намерения. Достаточно добавить скобки — и всё заработает:

Разные группировки — разные ответы. Скобки здесь не для красоты: они говорят и парсеру, и тому, кто будет читать код после вас, что вы на самом деле имели в виду.

Это не то же самое, что значение по умолчанию в параметре функции

У параметров по умолчанию в сигнатуре функции своё правило: они срабатывают только тогда, когда аргумент равен undefined, но не null. И это тонкое отличие от ??, который одинаково реагирует на оба случая.

Если нужно, чтобы null тоже запускал значение по умолчанию, используйте ?? прямо в теле функции:

Небольшая, но легко обжигающая разница — особенно когда API возвращает null в значении «ничего не задано».

Когда использовать ??

По умолчанию берите именно ??, если нет веской причины поступить иначе. Этот оператор лучше всего ложится на то, как данные ведут себя в реальности: 0, '' и false — это, как правило, осмысленные значения, которые стоит сохранить, а подменять на дефолт нужно только по-настоящему отсутствующие данные. || оставьте для случаев, когда вам действительно нужно заменить любое falsy-значение.

Дальше: классы

На этом с объектами и массивами закончили — у вас уже есть всё, чтобы безопасно собирать структуры данных и ходить по ним. Следующая глава — про организацию поведения: классы, наследование и прототипная система, которая лежит в их основе.

Часто задаваемые вопросы

Что такое оператор нулевого слияния в JavaScript?

?? возвращает правый операнд только если левый равен null или undefined. Во всех остальных случаях возвращается левый операнд как есть. Запись value ?? fallback — это стандартный способ задать значение по умолчанию, не спотыкаясь на таких валидных, но falsy-значениях, как 0 или пустая строка ''.

В чём разница между ?? и || в JavaScript?

|| срабатывает на любом falsy-значении: 0, '', false, NaN, null, undefined. А ?? — только на null и undefined. Если 0 или пустая строка для ваших данных — это нормальное, осмысленное значение, нужен именно ??. А вот когда действительно хочется отсечь все falsy-значения разом, || по-прежнему к месту.

Можно ли сочетать ?? с опциональной цепочкой ?.?

Да, это классическая связка. Выражение user?.settings?.theme ?? 'light' безопасно проходит по цепочке, возвращает undefined, если где-то по пути значения нет, и тут же подставляет 'light'. Оба оператора не случайно появились вместе в ES2020 — именно под этот сценарий.

Что делает оператор ??=?

a ??= b присваивает b переменной a, только если в a сейчас лежит null или undefined. По сути это короткая запись для a = a ?? b, но с короткозамкнутой логикой: если в a уже что-то есть, правая часть даже не вычислится. Удобно для того, чтобы дозаполнять недостающие поля в объекте настроек.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ