Avatar ·

Существует ли в JavaScript оператор нулевого слияния (null coalescing)?

Всем привет! Я начинающий JS-разработчик, и у меня возникла проблема с обработкой значений, которые могут быть null или undefined. Допустим, у меня есть функция, которая получает настройки пользователя, и если какое-то свойство отсутствует, я хочу подставить значение по умолчанию. Сейчас я делаю так: ```javascript function getSettings(user) { const theme = user.theme || 'light'; const fontSize = user.fontSize || 16; return { theme, fontSize }; } ``` Но это работает не совсем правильно. Например, если пользователь явно указал `theme: ''` (пустая строка) или `fontSize: 0`, то оператор `||` воспринимает их как falsy и подставляет значение по умолчанию. А мне нужно, чтобы подстановка происходила только если значение действительно null или undefined. Я слышал, что в некоторых языках (например, в C# или PHP) есть специальный оператор "null coalescing" (??), который делает именно то, что мне нужно — проверяет только на null/undefined, а не на все falsy значения. Есть ли что-то подобное в JavaScript? Или как правильно обрабатывать такие случаи? Буду благодарен за примеры кода и объяснения! Я использую современный JavaScript (ES6+), если это важно. <<>>

Да, в JavaScript есть оператор нулевого слияния (Nullish Coalescing Operator)

Начиная с ECMAScript 2020 (ES11), в JavaScript появился оператор ??, который работает именно так, как вы ищете. Он называется nullish coalescing operator (оператор нулевого слияния).

В отличие от логического оператора ИЛИ (||), который подставляет значение по умолчанию для любого falsy-значения (false, 0, '', null, undefined, NaN), оператор ?? реагирует только на nullish значения — то есть только на null и undefined.

Как это работает

// Синтаксис: leftExpr ?? rightExpr
const result = value ?? defaultValue;
// Если value не null и не undefined, вернется value
// Иначе вернется defaultValue

Примеры для вашей задачи

function getSettings(user) {
  // Теперь пустая строка '' и 0 НЕ будут заменяться
  const theme = user.theme ?? 'light';     // '' останется как ''
  const fontSize = user.fontSize ?? 16;    // 0 останется как 0
  const language = user.language ?? 'en';  // undefined -> 'en'
  const debug = user.debug ?? false;       // null -> false
  return { theme, fontSize, language, debug };
}

// Проверка:
console.log(getSettings({ theme: '', fontSize: 0 }));
// { theme: '', fontSize: 0, language: 'en', debug: false }

console.log(getSettings({}));
// { theme: 'light', fontSize: 16, language: 'en', debug: false }

console.log(getSettings({ theme: 'dark', fontSize: null }));
// { theme: 'dark', fontSize: 16, language: 'en', debug: false }

Важные особенности оператора ??

  • Нельзя использовать вместе с || или && без скобок. JavaScript выдаст ошибку:
    // Ошибка!
    const x = a ?? b || c;  // SyntaxError
    
    // Правильно:
    const x = (a ?? b) || c;
    const y = a ?? (b || c);
  • Работает с цепочками опциональной последовательности (optional chaining). Это очень удобно для глубоко вложенных объектов:
    const city = user?.address?.city ?? 'Неизвестный город';
  • Поддерживается во всех современных браузерах и Node.js 14+. Для старых окружений можно использовать Babel или TypeScript (который поддерживает ?? с версии 3.7).

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

  • Используйте ??, когда нужно различать явно заданные falsy-значения (пустая строка, 0, false) и отсутствие значения (null/undefined).
  • Используйте ||, когда любое falsy-значение должно приводить к подстановке значения по умолчанию (например, для флагов или строк, где пустая строка недопустима).

Полный пример с разными сценариями

// Сравнение ?? и ||
const tests = [
  { value: null, label: 'null' },
  { value: undefined, label: 'undefined' },
  { value: false, label: 'false' },
  { value: 0, label: '0' },
  { value: '', label: 'пустая строка' },
  { value: 'текст', label: 'непустая строка' },
  { value: 42, label: 'число 42' },
];

tests.forEach(({ value, label }) => {
  const orResult = value || 'DEFAULT';
  const nullishResult = value ?? 'DEFAULT';
  console.log(`${label}: || -> ${orResult}, ?? -> ${nullishResult}`);
});

// Вывод:
// null: || -> DEFAULT, ?? -> DEFAULT
// undefined: || -> DEFAULT, ?? -> DEFAULT
// false: || -> DEFAULT, ?? -> false
// 0: || -> DEFAULT, ?? -> 0
// пустая строка: || -> DEFAULT, ?? -> 
// непустая строка: || -> текст, ?? -> текст
// число 42: || -> 42, ?? -> 42

Как видите, оператор ?? оставляет false, 0 и пустую строку нетронутыми, что часто бывает критически важно для корректной работы приложения.

<<>>

Альтернативный подход: тернарный оператор и проверка на null/undefined

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

// Вариант 1: Тернарный оператор
const theme = user.theme !== null && user.theme !== undefined 
  ? user.theme 
  : 'light';

// Вариант 2: Функция-хелпер
function nullishCoalesce(value, defaultValue) {
  return value !== null && value !== undefined ? value : defaultValue;
}

const fontSize = nullishCoalesce(user.fontSize, 16);

// Вариант 3: Использование оператора ?? с полифиллом
// Если вы используете Babel, он автоматически транспилирует ?? в тернарные операторы

Дополнительный совет: Optional Chaining

Для безопасного доступа к вложенным свойствам используйте оператор опциональной последовательности ?. вместе с ??:

// Без optional chaining приходилось писать:
const city = user && user.address && user.address.city 
  ? user.address.city 
  : 'Unknown';

// С optional chaining + nullish coalescing:
const city = user?.address?.city ?? 'Unknown';

Эти два оператора (?? и ?.) отлично дополняют друг друга и делают код значительно чище и безопаснее.