Вы получаете данные пользователя из GitHub API. Одни пользователи указали компанию, другие — нет. Если написать user.organization.login, код упадёт с ошибкой для пользователей без организации. Раньше приходилось писать длинные защитные проверки. Опциональная цепочка решает это элегантно.
Вместо цепочки &&-проверок оператор ?. безопасно «идёт» по цепочке свойств — и если встречает null или undefined, возвращает undefined вместо ошибки.
const user = { name: 'Алексей' } // нет поля address
// Падает с ошибкой:
console.log(user.address.city)
// TypeError: Cannot read properties of undefined (reading 'city')
// Старый способ — многословно:
const city = user && user.address && user.address.city && user.address.city.toUpperCase()// Новый способ — чисто и коротко:
const city = user?.address?.city?.toUpperCase()
// Если user.address === undefined → вернёт undefined, без ошибкиДоступ к свойству:
user?.profile?.avatar // если user или profile — null/undefined → undefinedВызов метода:
user?.getAvatar?.() // если метода нет — не упадёт, вернёт undefinedДоступ к элементу массива:
arr?.[0] // если arr — null/undefined → undefined
users?.[0]?.name // первый пользователь, если массив не пустДинамические ключи:
const key = 'name'
user?.[key] // user?.name через динамический ключ// ?. возвращает undefined если путь оборван
// ?? задаёт значение по умолчанию вместо undefined/null
const city = user?.address?.city ?? 'Не указан'
const plan = user?.subscription?.plan ?? 'free'
const count = data?.items?.length ?? 0Ошибка 1: ?. слева от присваивания
// Сломано — нельзя использовать ?. слева от =
user?.address = { city: 'Москва' } // SyntaxError!
// Исправлено:
if (user) user.address = { city: 'Москва' }Ошибка 2: использование ?. везде без нужды
// Избыточно — если user всегда существует, ?. здесь лишний
function greet(user) {
return user?.name // user точно есть — параметр функции
}
// Лучше явно:
function greet(user) {
return user.name
}Ошибка 3: путаница с || и ??
// || вернёт правый операнд при любом falsy: 0, '', false
const count = user?.items?.length || 0 // если length = 0 → вернёт 0 — OK здесь
const name = user?.profile?.name || 'Аноним' // если name = '' → 'Аноним'!
// ?? вернёт правый только при null/undefined
const name = user?.profile?.name ?? 'Аноним' // '' останется ''response?.data?.user?.email — типично при работе с REST/GraphQLdocument.querySelector('.btn')?.addEventListener(...) — если элемент может отсутствоватьprops.user?.avatar ?? defaultAvatarconfig?.database?.host ?? 'localhost'Обработка разных форматов ответа GitHub API
// Симуляция ответов API — разные пользователи имеют разные поля
const users = [
{
login: 'torvalds',
name: 'Linus Torvalds',
company: 'Linux Foundation',
location: 'Portland, OR',
plan: { name: 'pro', seats: 1 },
},
{
login: 'ghost',
name: null,
company: null,
location: null,
plan: null,
},
{
login: 'newbie',
name: 'Начинающий',
// нет company, location, plan
},
]
function formatUser(user) {
return {
login: user.login,
name: user.name ?? 'Без имени',
company: user.company ?? 'Не указана',
city: user.location?.split(',')[0] ?? 'Не указан',
plan: user.plan?.name ?? 'free',
seats: user.plan?.seats ?? 1,
}
}
users.forEach(u => {
const info = formatUser(u)
console.log(`@${info.login}: ${info.name} | ${info.company} | план: ${info.plan}`)
})
// @torvalds: Linus Torvalds | Linux Foundation | план: pro
// @ghost: Без имени | Не указана | план: free
// @newbie: Начинающий | Не указана | план: free
// Безопасный вызов метода
const firstUser = users[0]
const upperCity = firstUser.location?.toUpperCase?.() ?? 'НЕТ'
console.log(upperCity) // 'PORTLAND, OR'Вы получаете данные пользователя из GitHub API. Одни пользователи указали компанию, другие — нет. Если написать user.organization.login, код упадёт с ошибкой для пользователей без организации. Раньше приходилось писать длинные защитные проверки. Опциональная цепочка решает это элегантно.
Вместо цепочки &&-проверок оператор ?. безопасно «идёт» по цепочке свойств — и если встречает null или undefined, возвращает undefined вместо ошибки.
const user = { name: 'Алексей' } // нет поля address
// Падает с ошибкой:
console.log(user.address.city)
// TypeError: Cannot read properties of undefined (reading 'city')
// Старый способ — многословно:
const city = user && user.address && user.address.city && user.address.city.toUpperCase()// Новый способ — чисто и коротко:
const city = user?.address?.city?.toUpperCase()
// Если user.address === undefined → вернёт undefined, без ошибкиДоступ к свойству:
user?.profile?.avatar // если user или profile — null/undefined → undefinedВызов метода:
user?.getAvatar?.() // если метода нет — не упадёт, вернёт undefinedДоступ к элементу массива:
arr?.[0] // если arr — null/undefined → undefined
users?.[0]?.name // первый пользователь, если массив не пустДинамические ключи:
const key = 'name'
user?.[key] // user?.name через динамический ключ// ?. возвращает undefined если путь оборван
// ?? задаёт значение по умолчанию вместо undefined/null
const city = user?.address?.city ?? 'Не указан'
const plan = user?.subscription?.plan ?? 'free'
const count = data?.items?.length ?? 0Ошибка 1: ?. слева от присваивания
// Сломано — нельзя использовать ?. слева от =
user?.address = { city: 'Москва' } // SyntaxError!
// Исправлено:
if (user) user.address = { city: 'Москва' }Ошибка 2: использование ?. везде без нужды
// Избыточно — если user всегда существует, ?. здесь лишний
function greet(user) {
return user?.name // user точно есть — параметр функции
}
// Лучше явно:
function greet(user) {
return user.name
}Ошибка 3: путаница с || и ??
// || вернёт правый операнд при любом falsy: 0, '', false
const count = user?.items?.length || 0 // если length = 0 → вернёт 0 — OK здесь
const name = user?.profile?.name || 'Аноним' // если name = '' → 'Аноним'!
// ?? вернёт правый только при null/undefined
const name = user?.profile?.name ?? 'Аноним' // '' останется ''response?.data?.user?.email — типично при работе с REST/GraphQLdocument.querySelector('.btn')?.addEventListener(...) — если элемент может отсутствоватьprops.user?.avatar ?? defaultAvatarconfig?.database?.host ?? 'localhost'Обработка разных форматов ответа GitHub API
// Симуляция ответов API — разные пользователи имеют разные поля
const users = [
{
login: 'torvalds',
name: 'Linus Torvalds',
company: 'Linux Foundation',
location: 'Portland, OR',
plan: { name: 'pro', seats: 1 },
},
{
login: 'ghost',
name: null,
company: null,
location: null,
plan: null,
},
{
login: 'newbie',
name: 'Начинающий',
// нет company, location, plan
},
]
function formatUser(user) {
return {
login: user.login,
name: user.name ?? 'Без имени',
company: user.company ?? 'Не указана',
city: user.location?.split(',')[0] ?? 'Не указан',
plan: user.plan?.name ?? 'free',
seats: user.plan?.seats ?? 1,
}
}
users.forEach(u => {
const info = formatUser(u)
console.log(`@${info.login}: ${info.name} | ${info.company} | план: ${info.plan}`)
})
// @torvalds: Linus Torvalds | Linux Foundation | план: pro
// @ghost: Без имени | Не указана | план: free
// @newbie: Начинающий | Не указана | план: free
// Безопасный вызов метода
const firstUser = users[0]
const upperCity = firstUser.location?.toUpperCase?.() ?? 'НЕТ'
console.log(upperCity) // 'PORTLAND, OR'Интернет-магазин получает данные о заказе из API — некоторые поля могут отсутствовать. Напиши функцию getOrderInfo(order), которая возвращает объект с полями: customerName (имя покупателя или 'Гость'), city (город доставки или 'Самовывоз'), promoDiscount (скидка промокода или 0), firstItemName (название первого товара или 'Нет товаров'). Используй ?. и ??.
customerName: order?.customer?.name ?? "Гость". firstItemName: order?.items?.[0]?.name ?? "Нет товаров". promoDiscount: order?.promo?.discount ?? 0