Menu
Русский

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

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

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

Меняем на ??:

index.js
Output
Click Run to see the output here.

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

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

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

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

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

index.js
Output
Click Run to see the output here.

Небольшая, но легко обжигающая разница — особенно когда 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

НАЧАТЬ