← JavaScript/Оператор нулевого слияния ??#61 из 383← ПредыдущийСледующий →+15 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

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

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

На основе предыдущих уроков

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

Какую проблему решает ??

|| заменяет любое falsy: false, 0, '', null, undefined`

?? заменяет только "настоящее отсутствие": null и undefined

// Проблема с ||
const volume = 0
const displayVolume = volume || 50  // 50 — неверно! Пользователь выключил звук

// Решение с ??
const displayVolume2 = volume ?? 50  // 0 — правильно

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

Используй `??` когда хочешь подставить дефолт только если данных нет совсем:

  • Настройки пользователя (которые могут быть 0, false, '')
  • Результат API (может вернуть null)
  • Параметры функции
  • Используй `||` когда хочешь заменить любое "пустое/ложное" значение:

  • Значение по умолчанию для отображения ("Гость" вместо пустой строки)
  • ??= (оператор логического присваивания nullish)

    Присваивает только если переменная null или undefined:

    let config = null
    config ??= { theme: 'dark', fontSize: 14 }
    // config = { theme: 'dark', fontSize: 14 }
    
    let existing = { theme: 'light', fontSize: 12 }
    existing ??= { theme: 'dark', fontSize: 14 }
    // existing остаётся { theme: 'light', fontSize: 12 }
    
    // Эквивалентно:
    // config = config ?? { theme: 'dark', fontSize: 14 }

    Вместе с ?. (optional chaining)

    ?? и ?. часто работают в паре:

    const user = { profile: null }
    
    // Без ?. — ошибка:
    // user.profile.city  // TypeError: Cannot read property 'city' of null
    
    // С ?. — безопасно:
    const city = user?.profile?.city ?? 'Не указан'
    // null?.city → undefined, undefined ?? 'Не указан' → 'Не указан'

    Типичные ошибки

    Ошибка 1: Использовать || там где нужен ?? для нулей

    const itemsPerPage = 0  // "показывать все товары" в настройке
    const limit = itemsPerPage || 20  // 20 — баг! 0 заменён на дефолт
    const limit2 = itemsPerPage ?? 20  // 0 — правильно

    Ошибка 2: Смешивать ?? с && или || без скобок

    // SyntaxError — нельзя смешивать без скобок!
    a ?? b || c   // Ошибка
    a ?? (b || c) // Правильно
    (a ?? b) || c // Тоже правильно

    Ошибка 3: ?? не работает с обычными falsy как ожидается

    const name = ''
    const display = name ?? 'Аноним'  // '' — пустая строка не заменится!
    // Если хочешь заменить пустую строку — используй ||
    const display2 = name || 'Аноним'  // 'Аноним' — правильно для этого случая

    В реальных проектах

    ?? используется повсеместно при работе с данными из API (поля могут быть null), при инициализации конфигурации, и в React для условного рендеринга числовых данных (count ?? 0).

    Примеры

    Инициализация настроек приложения с дефолтными значениями

    function initAppSettings(userSettings) {
      // ?? заменяет только null/undefined — сохраняет 0, false, ''
      return {
        theme:        userSettings.theme        ?? 'light',
        fontSize:     userSettings.fontSize     ?? 16,
        volume:       userSettings.volume       ?? 0.8,
        itemsPerPage: userSettings.itemsPerPage ?? 20,
        language:     userSettings.language     ?? 'ru',
        notifications: userSettings.notifications ?? true,
      }
    }
    
    // Пользователь выключил звук и задал 0 элементов на странице
    const userSettings = { theme: 'dark', volume: 0, itemsPerPage: 0 }
    const settings = initAppSettings(userSettings)
    
    console.log(settings.theme)        // 'dark'   (сохранили)
    console.log(settings.volume)       // 0        (!! ?? сохранил ноль)
    console.log(settings.itemsPerPage) // 0        (!! ?? сохранил ноль)
    console.log(settings.fontSize)     // 16       (подставил дефолт)
    console.log(settings.language)     // 'ru'     (подставил дефолт)
    
    // Если бы использовали ||:
    const buggySettings = {
      volume: userSettings.volume || 0.8,  // 0.8 — баг! пользователь хотел тишину
    }
    console.log('Баг с ||:', buggySettings.volume)  // 0.8 — неверно!

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

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

    На основе предыдущих уроков

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

    Какую проблему решает ??

    || заменяет любое falsy: false, 0, '', null, undefined`

    ?? заменяет только "настоящее отсутствие": null и undefined

    // Проблема с ||
    const volume = 0
    const displayVolume = volume || 50  // 50 — неверно! Пользователь выключил звук
    
    // Решение с ??
    const displayVolume2 = volume ?? 50  // 0 — правильно

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

    Используй `??` когда хочешь подставить дефолт только если данных нет совсем:

  • Настройки пользователя (которые могут быть 0, false, '')
  • Результат API (может вернуть null)
  • Параметры функции
  • Используй `||` когда хочешь заменить любое "пустое/ложное" значение:

  • Значение по умолчанию для отображения ("Гость" вместо пустой строки)
  • ??= (оператор логического присваивания nullish)

    Присваивает только если переменная null или undefined:

    let config = null
    config ??= { theme: 'dark', fontSize: 14 }
    // config = { theme: 'dark', fontSize: 14 }
    
    let existing = { theme: 'light', fontSize: 12 }
    existing ??= { theme: 'dark', fontSize: 14 }
    // existing остаётся { theme: 'light', fontSize: 12 }
    
    // Эквивалентно:
    // config = config ?? { theme: 'dark', fontSize: 14 }

    Вместе с ?. (optional chaining)

    ?? и ?. часто работают в паре:

    const user = { profile: null }
    
    // Без ?. — ошибка:
    // user.profile.city  // TypeError: Cannot read property 'city' of null
    
    // С ?. — безопасно:
    const city = user?.profile?.city ?? 'Не указан'
    // null?.city → undefined, undefined ?? 'Не указан' → 'Не указан'

    Типичные ошибки

    Ошибка 1: Использовать || там где нужен ?? для нулей

    const itemsPerPage = 0  // "показывать все товары" в настройке
    const limit = itemsPerPage || 20  // 20 — баг! 0 заменён на дефолт
    const limit2 = itemsPerPage ?? 20  // 0 — правильно

    Ошибка 2: Смешивать ?? с && или || без скобок

    // SyntaxError — нельзя смешивать без скобок!
    a ?? b || c   // Ошибка
    a ?? (b || c) // Правильно
    (a ?? b) || c // Тоже правильно

    Ошибка 3: ?? не работает с обычными falsy как ожидается

    const name = ''
    const display = name ?? 'Аноним'  // '' — пустая строка не заменится!
    // Если хочешь заменить пустую строку — используй ||
    const display2 = name || 'Аноним'  // 'Аноним' — правильно для этого случая

    В реальных проектах

    ?? используется повсеместно при работе с данными из API (поля могут быть null), при инициализации конфигурации, и в React для условного рендеринга числовых данных (count ?? 0).

    Примеры

    Инициализация настроек приложения с дефолтными значениями

    function initAppSettings(userSettings) {
      // ?? заменяет только null/undefined — сохраняет 0, false, ''
      return {
        theme:        userSettings.theme        ?? 'light',
        fontSize:     userSettings.fontSize     ?? 16,
        volume:       userSettings.volume       ?? 0.8,
        itemsPerPage: userSettings.itemsPerPage ?? 20,
        language:     userSettings.language     ?? 'ru',
        notifications: userSettings.notifications ?? true,
      }
    }
    
    // Пользователь выключил звук и задал 0 элементов на странице
    const userSettings = { theme: 'dark', volume: 0, itemsPerPage: 0 }
    const settings = initAppSettings(userSettings)
    
    console.log(settings.theme)        // 'dark'   (сохранили)
    console.log(settings.volume)       // 0        (!! ?? сохранил ноль)
    console.log(settings.itemsPerPage) // 0        (!! ?? сохранил ноль)
    console.log(settings.fontSize)     // 16       (подставил дефолт)
    console.log(settings.language)     // 'ru'     (подставил дефолт)
    
    // Если бы использовали ||:
    const buggySettings = {
      volume: userSettings.volume || 0.8,  // 0.8 — баг! пользователь хотел тишину
    }
    console.log('Баг с ||:', buggySettings.volume)  // 0.8 — неверно!

    Задание

    Ты пишешь функцию для отображения профиля в мессенджере. Функция getUserLabel возвращает строку для показа: имя если есть, иначе никнейм если есть, иначе "Пользователь". Используй ??, потому что имя может быть null (не указано), но не должно быть заменено при пустой строке.

    Подсказка

    user.name ?? user.nickname ?? "Пользователь" — цепочка из двух ?? подставляет каждый следующий только если предыдущий null/undefined

    Загружаем среду выполнения...
    Загружаем AI-помощника...