W3docs

Оператор нулевого слияния в JavaScript (??)

Оператор нулевого слияния (??): отличие от ||, приоритет операторов и логические операторы присваивания ??=, &&= и ||=.

Оператор нулевого слияния (??) — это краткий и выразительный способ задать значение по умолчанию в JavaScript. Он возвращает правый операнд только тогда, когда левый операнд является нулевым — то есть null или undefined — а во всех остальных случаях возвращает левое значение без изменений. Появившись в ES2020, он решает давнюю проблему использования оператора || для задания значений по умолчанию и отлично сочетается с опциональной цепочкой и уже знакомыми вам логическими операторами.

Что означает «нулевое» значение

Значение считается нулевым, если оно в точности равно null или undefined. Любое другое значение — включая ложные (0, '', false, NaN) — воспринимается оператором ?? как настоящее, намеренное значение.

null ?? 'default'       // 'default'
undefined ?? 'default'  // 'default'

0 ?? 'default'          // 0
'' ?? 'default'         // ''
false ?? 'default'      // false
NaN ?? 'default'        // NaN

Правило простое: если левая часть равна null или undefined, возвращается правая часть; в противном случае — левая.

javascript— editable

Чем ?? отличается от ||

Именно ради этого и появился оператор ??. Логический оператор ИЛИ (||) возвращает правую часть всякий раз, когда левая часть является любым ложным значением — 0, '', false, NaN, null и undefined — все они подпадают под это условие. Такое поведение приемлемо до тех пор, пока 0 или пустая строка не являются допустимыми значениями; в противном случае || тихо выдаёт неверный результат.

Рассмотрим счётчик, допустимым значением которого может быть 0:

let count = 0;

let withOr = count || 100;   // 100  — wrong! 0 got replaced
let withNullish = count ?? 100; // 0   — correct, 0 was preserved

Поскольку 0 является ложным значением, || обрабатывает его так же, как «нет значения», и переходит к 100. Оператор ?? распознаёт, что 0 не является нулевым, и сохраняет его. Та же ловушка применима к пустым строкам:

javascript— editable
Внимание

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

Приоритет и скобки

Оператор ?? имеет низкий приоритет — примерно такой же, как у ||, и ниже, чем у арифметических и операторов сравнения. На практике это означает, что запасное значение вычисляется прежде, чем результат используется в большем выражении, однако для ясности намерений всё же рекомендуется использовать скобки.

let height = null;
let width;

// Each ?? is evaluated, then the products are multiplied
let area = (height ?? 100) * (width ?? 50);
console.log(area); // 100 * 50 = 5000

Без скобок умножение может связываться теснее, чем вы ожидаете, поэтому оборачивание каждого значения по умолчанию в ( ... ) делает вычисления однозначными и читаемыми.

Совместное использование ?? с && или || запрещено

JavaScript намеренно не позволяет смешивать ?? с && или || в одном выражении без скобок. Это вызывает SyntaxError, поскольку приоритет между ними был бы неоднозначным и легко допускал бы ошибки при чтении.

// SyntaxError: Unexpected token '||'
let result = a ?? b || c;

// Fix it by grouping explicitly:
let safe = (a ?? b) || c;   // valid
let other = a ?? (b || c);  // also valid, different meaning

Два допустимых варианта имеют разный смысл — именно поэтому язык заставляет вас сделать выбор. Добавьте скобки и точно укажите, что именно вы имеете в виду.

Примечание

Ограничение применяется только тогда, когда ?? стоит непосредственно рядом с && или ||. Вы можете свободно использовать их в отдельных выражениях, а скобки всегда устраняют неоднозначность.

Логические операторы присваивания

В ES2021 добавлены три логических оператора присваивания, которые совмещают присваивание с логической проверкой. Они выполняют присваивание левой переменной только при выполнении определённого условия, что делает их идеальными для задания значений по умолчанию и условных обновлений.

Присваивание при нулевом значении (??=)

x ??= y присваивает y переменной x только если x в данный момент равна null или undefined. Это форма присваивания для ?? и наиболее распространённый из трёх операторов для установки значений по умолчанию в объекте.

let settings = { theme: 'dark' };

settings.theme ??= 'light';   // already set → stays 'dark'
settings.fontSize ??= 16;     // missing → becomes 16

console.log(settings); // { theme: 'dark', fontSize: 16 }

Логическое присваивание ИЛИ (||=)

x ||= y присваивает y переменной x, когда x является ложным значением (0, '', false, null, undefined, NaN). Как и ||, он может перезаписывать допустимые ложные значения, поэтому используйте его только тогда, когда любое ложное значение действительно означает «замени меня».

javascript— editable

Логическое присваивание И (&&=)

x &&= y присваивает y переменной x только когда x является истинным значением. Это удобно для обновления уже существующего значения без его создания.

let config = { token: 'abc123' };

config.token &&= config.token.trim(); // truthy → trimmed and reassigned
config.missing &&= 'never set';        // undefined → left untouched

console.log(config); // { token: 'abc123' }

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

Примеры из реальной практики

Оператор ?? особенно полезен там, где значение может законно отсутствовать, но 0 или '' должны сохраняться. Типичные сценарии включают чтение конфигурации, задание значений по умолчанию для необязательных параметров функции и извлечение полей из ответов API.

// 1. Default options merged with user input
function createButton(options) {
  let label = options.label ?? 'Submit';
  let count = options.count ?? 0; // 0 stays 0, never becomes a default
  return { label, count };
}

// 2. Reading a possibly-missing API field
let response = { data: { user: { nickname: null } } };
let name = response.data.user.nickname ?? 'Guest';
console.log(name); // 'Guest'

Оператор отлично сочетается с опциональной цепочкой (?.). Опциональная цепочка безопасно возвращает undefined, когда часть пути отсутствует, а ?? затем задаёт запасное значение в одну лаконичную строку:

let city = user?.address?.city ?? 'Unknown';

Это читается так: «Используй город пользователя, если вся цепочка существует, иначе используй 'Unknown'.» Для выбора одного из двух путей на основе условия лучше подходит условный (тернарный) оператор?? предназначен специально для задания значений по умолчанию при нулевых значениях.

Информация

Поддержка браузерами: ?? входит в состав ES2020, а логические операторы присваивания (??=, ||=, &&=) появились в ES2021. Все современные браузеры и Node.js 14+ поддерживают их. Если необходима поддержка очень старых окружений, транспилятор наподобие Babel может преобразовать их.

Итоги

  • ?? возвращает правый операнд только когда левый равен null или undefined; в остальных случаях возвращает левый.
  • Предпочитайте ?? вместо || для задания значений по умолчанию, когда 0, '' или false являются допустимыми значениями.
  • ?? имеет низкий приоритет — группируйте с помощью скобок и никогда не ставьте его непосредственно рядом с && или || без скобок (это вызовет SyntaxError).
  • ??=, ||= и &&= выполняют присваивание условно: при нулевом, при ложном и при истинном значении соответственно.
  • Комбинируйте ?? с опциональной цепочкой (?.) для чтения потенциально отсутствующих данных и задания значения по умолчанию в одном выражении.

Проверьте свои знания

Практика
Чему равно выражение 0 ?? 100?
Чему равно выражение 0 ?? 100?
Практика
Почему запись a ?? b || c без скобок является проблемой?
Почему запись a ?? b || c без скобок является проблемой?
Практика
Что делает user.role ??= 'member', если user.role равно undefined?
Что делает user.role ??= 'member', если user.role равно undefined?
Was this page helpful?