В настройках приложения пользователь может установить размер шрифта в 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 — правильноИспользуй `??` когда хочешь подставить дефолт только если данных нет совсем:
Используй `||` когда хочешь заменить любое "пустое/ложное" значение:
Присваивает только если переменная 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 }?? и ?. часто работают в паре:
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 — правильноИспользуй `??` когда хочешь подставить дефолт только если данных нет совсем:
Используй `||` когда хочешь заменить любое "пустое/ложное" значение:
Присваивает только если переменная 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 }?? и ?. часто работают в паре:
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