Оператор нулевого слияния в 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, возвращается правая часть; в противном случае — левая.
Чем ?? отличается от ||
Именно ради этого и появился оператор ??. Логический оператор ИЛИ (||) возвращает правую часть всякий раз, когда левая часть является любым ложным значением — 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 не является нулевым, и сохраняет его. Та же ловушка применима к пустым строкам:
Используйте ?? вместо ||, когда 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). Как и ||, он может перезаписывать допустимые ложные значения, поэтому используйте его только тогда, когда любое ложное значение действительно означает «замени меня».
Логическое присваивание И (&&=)
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).??=,||=и&&=выполняют присваивание условно: при нулевом, при ложном и при истинном значении соответственно.- Комбинируйте
??с опциональной цепочкой (?.) для чтения потенциально отсутствующих данных и задания значения по умолчанию в одном выражении.