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

объекты≈ 7 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Данные из реального мира редко бывают полными: у одного пользователя есть адрес, у другого его никто не заполнил. Оператор ?. — это точка, которая умеет вовремя остановиться: вместо падения программы она честно возвращает undefined.

Разделы темы:·

Читать свойство у объекта — привычная операция: user.name. Но если по пути встретится значение, которого нет, точка перестаёт быть безобидной. Обращение к свойству undefined или null — это не «пусто», а ошибка выполнения, после которой скрипт останавливается.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Обратите внимание на разницу: user.address спокойно вернул undefined — одно отсутствующее свойство читать можно. А вот undefined.city — уже TypeError. Мы печатаем e.constructor.name, а не текст ошибки: формулировка отличается от браузера к браузеру, а имя типа везде одинаковое.

До 2020 года это лечили длинными проверками через &&: пока каждое звено «правдиво», идём дальше. Работает, но имя объекта приходится повторять столько раз, сколько в цепочке точек.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Опциональная цепочка ?. заменяет всю эту конструкцию. Правило одно: если значение слева от ?. равно null или undefined, выражение немедленно возвращает undefined. Во всех остальных случаях ?. ведёт себя как обычная точка и не добавляет никакой магии.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Важно, что проверяются только эти два значения. Ноль, пустая строка, false и NaN для ?. — совершенно нормальные значения, цепочка на них не обрывается. Этим ?. отличается от старой проверки через &&, которая спотыкалась на любом «ложном» звене.

Читайте ?. вслух как «если есть — иди дальше, если нет — верни undefined и молчи».

?. спасает от отсутствующего значения, но не от отсутствующей переменной. Если имени нет в области видимости, получите ReferenceError ещё до того, как оператор вступит в дело.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

?. не просто подставляет undefined вместо результата — он обрывает вычисление. Как только слева обнаружились null или undefined, вся правая часть выражения пропускается: не читаются свойства, не вычисляются ключи, не вызываются функции. Это и называется коротким замыканием.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Замыкание останавливает только текущую цепочку, а не всё выражение целиком. В строке user?.name + "!" сложение выполнится в любом случае: сначала цепочка даст undefined, а потом к нему приклеится восклицательный знак. Скобки задают границу: (user?.name).length уже упадёт с ошибкой.

Свойства — не единственное место, где может не оказаться значения. Метода может не быть у объекта, а ключ может приходить переменной. Для этого есть ещё две формы: ?.() перед вызовом и ?.[] перед чтением по вычисляемому ключу.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

?.() проверяет саму функцию: если guest.say не существует, вызова не будет и мы получим undefined вместо «say is not a function». Форма ?.[] устроена так же, только вместо вызова — доступ по ключу в квадратных скобках.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
ЗаписьЧто проверяетсяРезультат при null/undefined
obj?.propсам obj перед чтением свойстваundefined
obj?.[key]сам obj перед чтением по ключуundefined
fn?.()сама fn перед вызовомundefined

?. делает код короче, но не делает его умнее. Он проверяет ровно одно значение — то, что стоит слева от него, — и ничего не знает о смысле ваших данных. Самая частая ошибка новичка: поставить ?. в начале цепочки и решить, что дальше можно расслабиться.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Второй подвох — избыточность. Если объект обязан существовать по логике программы, писать ?. вредно: настоящая ошибка (сломанный ответ сервера, забытая инициализация) превратится в тихий undefined и всплывёт где-нибудь через десять строк. Ставьте ?. там, где отсутствие значения — нормальная ситуация, а не там, где просто страшно.

И наконец, ?. — оператор чтения. Слева от знака = он не работает: user?.name = "Боря" — синтаксическая ошибка. Зато он отлично дружит с ??, когда нужно подставить значение по умолчанию.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Раньше опечатка в имени свойства падала с громкой ошибкой на следующей же точке. С ?. она молча превращается в undefined — код «работает», просто ничего не показывает. Это ещё один довод не ставить оператор по всей цепочке подряд.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

  • obj?.prop возвращает undefined, если obj равен null или undefined, и работает как обычная точка во всех остальных случаях.
  • Проверяются только null и undefined: ноль, пустая строка и false цепочку не обрывают.
  • Короткое замыкание пропускает весь остаток цепочки — ключи не вычисляются, функции не вызываются.
  • Есть три формы: ?. для свойств, ?.[] для вычисляемых ключей, ?.() для вызова метода.
  • ?. защищает только то значение, что стоит слева; он не заменяет проверку логики и не годится для записи.

1/3Объект user = { name: "Аня" }. Что выведет console.log(user.address?.city)?

2/3let n = 0; const key = () => { n += 1; return "a"; }; После null?.[key()] чему равно n?

3/3У объекта user нет свойства address. Что сделает user?.address.city?

Достать город из неполных данных

Профили пользователей приходят с сервера в разной полноте: у кого-то заполнен адрес, у кого-то только имя, а иногда вместо объекта приходит null. Напишите функцию getCity(user), которая возвращает user.profile.address.city, а если по пути чего-то не хватает — строку "неизвестно". Ни одна проверка не должна падать с ошибкой.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Цепочку ?. ставим на каждом звене, которого может не быть, а подстановку значения по умолчанию отдаём оператору ?? — он сработает ровно на undefined и null.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Использовать здесь || вместо ?? было бы рискованно: пустая строка в поле города тоже заменилась бы на «неизвестно», хотя формально значение пришло.

Необязательный метод

Напишите функцию tryLog(obj, msg): если у объекта есть метод write, она вызывает его с сообщением и возвращает true; если метода или самого объекта нет — просто возвращает false, ничего не вызывая и не бросая ошибок.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Цепочка obj?.write достаёт свойство, не падая ни на отсутствующем объекте, ни на отсутствующем методе, а typeof решает, можно ли его звать.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Порядок здесь важен. Написать сначала obj?.write?.(msg), а проверку typeof оставить на возврат, нельзя: ?.() страхует только от null и undefined, и если в write лежит строка, вызов упадёт с TypeError — ровно та ошибка, которой задача запрещает случиться.