Перейти к содержимому
Теория · Объекты · урок 10 из 13

Опциональная цепочка

Представьте цепочку карточек: от анкеты пользователя к карточке с адресом, а от неё — к городу. У одних пользователей карточка с адресом есть, у других нет. Если карточки нет, искать в ней строку «Город» не получится.

Программа в таком случае останавливается:

JavaScript
const user = { name: "Ann" };

console.log(user.address); // undefined
console.log(user.address.city); // ✗
TEXT
TypeError: Cannot read properties of undefined (reading 'city')

Давайте разберём, что произошло:

  1. user.address даёт undefined: свойства address нет, но это ещё не ошибка.
  2. user.address.city читает .city у undefined. У undefined свойств нет, и чтение .city уже ошибка.

То же случится со значением null. Проверка через if перед каждой точкой сделала бы код длинным: if (user.address !== undefined) для каждого звена.

Оператор ?.

Опциональная цепочка — оператор ?. вместо точки. Если значение слева от ?. равно null или undefined, всё выражение даёт undefined, и ошибки нет. Иначе ?. читает свойство, как обычная точка:

JavaScript
const ann = { name: "Ann" };
const bob = { name: "Bob", address: { city: "Oslo" } };

console.log(ann.address?.city); // undefined
console.log(bob.address?.city); // Oslo
console.log(ann.address?.city ?? "unknown"); // unknown

Разберём по строкам:

  1. У ann адреса нет: ann.address равно undefined, и ?. останавливает чтение.
  2. У bob адрес есть, и ?.city читает город, как обычная точка.
  3. Последняя строка подставляет "unknown" вместо undefined.

В картотеке ?. похож на осторожного помощника. Он заглядывает в ящик, и если карточки там нет, отвечает «нет данных» вместо того, чтобы искать строку в пустоте.

Ещё две формы той же записи:

  • цепочка может проверять несколько звеньев: order?.client?.address;
  • для ключа из переменной есть запись со скобками: user.address?.[field].

Запасное значение через ??

Оператор ?? подставляет запасное значение, когда цепочка дала undefined. Здесь нужен именно ??, а не ||: с || пустая строка или 0 в свойстве тоже заменились бы запасным значением.

Разницу между ?? и || часто спрашивают на собеседовании. || заменяет любое ложное значение, а ?? — только null и undefined.

Возьмём квартиру на нулевом этаже: const flat = { floor: 0 }. Запись flat?.floor ?? 1 даёт 0, как и записано в данных. Запись flat?.floor || 1 даёт 1: оператор || принял ноль за отсутствие значения, и этаж потерялся.

Где остановится цепочка

Когда ?. встречает undefined, остаток цепочки справа не вычисляется: ann.address?.city.length тоже даёт undefined, без ошибки.

Почему нет ошибки на .length? ?. обрывает не одно звено, а всю цепочку справа от себя. Помощник, не найдя карточки, не идёт дальше по цепочке, а сразу возвращает ответ.

Когда ставить ?.

Ставьте ?. только там, где значения может не быть по смыслу данных. Если свойство обязано быть, обычная точка честно сообщит об ошибке, а ?. спрячет её за undefined.

Здесь аналогия с помощником перестаёт работать: через ?. можно только читать. Присваивать через ?. нельзя: user?.name = "Bob" — это SyntaxError.

Частая ошибка

?. поставили не на то звено цепочки:

JavaScript
const user = { name: "Ann" };

console.log(user?.address.city); // ✗
TEXT
TypeError: Cannot read properties of undefined (reading 'city')

?. проверяет только значение слева от себя. Здесь это user, а он существует, поэтому чтение идёт дальше, и .city читается у undefined.

Знак нужен после того свойства, которого может не быть: user.address?.city. Если сообщение то же, что и без ?., проверка стоит не на том звене.

Главное

  • a?.b даёт undefined, если a равно null или undefined, а иначе читает b, как точка.
  • Как только ?. встречает null или undefined, остаток цепочки справа не вычисляется.
  • ?? подставляет запасное значение только вместо null и undefined, не трогая 0 и пустую строку.
  • ?. ставят сразу после того звена, которого может не быть.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом