W3docs

Опциональная цепочка в JavaScript (?.)

Оператор ?. в JavaScript: безопасный доступ к вложенным свойствам object, вызов методов и чтение динамических ключей без ошибок.

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

Проблема: обращение к отсутствующим свойствам

Представьте, что у вас есть object user, и вы хотите прочитать город из вложенного object address. Если address отсутствует, обращение к его свойству выбрасывает ошибку:

let user = {}; // a user without an address

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

Причина в том, что user.address равно undefined, а читать .city из undefined нельзя. Это распространённая ситуация, когда данные поступают из API, формы или базы данных, где некоторые поля необязательны.

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

let user = {};

// The old way with the && operator
let city = user && user.address && user.address.city;
console.log(city); // undefined

// Or with an even longer ternary
let city2 = user ? (user.address ? user.address.city : undefined) : undefined;
console.log(city2); // undefined

Как работает опциональная цепочка

Оператор опциональной цепочки ?. останавливает вычисление, если значение перед ним равно null или undefined, и возвращает undefined вместо выброса ошибки. Такое поведение называется коротким замыканием (short-circuiting).

Вот тот же запрос, теперь безопасный и читаемый:

javascript— editable

Когда user.address равно undefined, выражение user?.address?.city тихо возвращает undefined. Когда данные присутствуют, цепочка читает всё до конца, вплоть до city.

Примечание

?. считает «отсутствующими» только null и undefined. Другие ложные значения, такие как 0, '' (пустая string) или false, являются настоящими значениями, поэтому цепочка продолжает работать через них в обычном режиме.

Он защищает только значение слева

Это самое важное правило, и новички чаще всего ошибаются именно здесь. ?. проверяет только значение непосредственно слева от него, а не всю цепочку.

Рассмотрим a?.b.c:

  • Если a равно null или undefined, всё выражение прерывается по короткому замыканию и возвращает undefined.
  • Но если a существует, а b равно null или undefined, то .c по-прежнему является обычным обращением — и оно выбросит ошибку.
let a = { b: null };

console.log(a?.b);    // null — fine, a exists
console.log(a?.b.c);  // TypeError: Cannot read properties of null (reading 'c')

Исправление: поставьте ?. в каждом месте, где значение может законно отсутствовать:

let a = { b: null };

console.log(a?.b?.c); // undefined — no error

Правило простое: ставьте ?. непосредственно после каждого значения, которого может не быть, а не только в начале цепочки.

Короткое замыкание останавливает всю цепочку

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

javascript— editable

Это полезная гарантия: ничто после отсутствующего звена не получит шанса выполниться или вызвать побочные эффекты.

Три формы опциональной цепочки

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

?. для доступа к свойствам

Используйте obj?.prop для чтения свойства, когда obj может быть null или undefined.

let user = { name: 'John' };

console.log(user?.name);    // John
console.log(user?.surname); // undefined (key missing, but no error)

?.() для вызова методов

Используйте obj.method?.() для вызова метода только в том случае, если он существует. Это удобно, когда object может поддерживать или не поддерживать данный метод.

javascript— editable

Если greet не является функцией (потому что она отсутствует), ?.() пропускает вызов и возвращает undefined вместо того, чтобы выбрасывать TypeError: greet is not a function.

?.[] для динамических ключей

Используйте obj?.[key], когда вы читаете свойство с помощью скобочной нотации — например, динамического ключа, хранящегося в переменной.

let user = { name: 'Alice' };
let key = 'name';

console.log(user?.[key]); // Alice

let nobody = null;
console.log(nobody?.[key]); // undefined (no error)

Опциональная цепочка предназначена для чтения, а не для записи

Вы можете использовать ?. для чтения значения и даже для безопасного удаления свойства с помощью delete. Но вы не можете использовать его в левой части присваивания.

let user = null;

// Reading is fine
console.log(user?.name); // undefined

// Deleting is fine — delete is skipped if user is null
delete user?.name; // no error

// Assignment does NOT work — this is a syntax error
// user?.name = 'John'; // SyntaxError

Причина в том, что присваивать нечему, когда левая часть отсутствует, поэтому язык это не допускает.

Не злоупотребляйте опциональной цепочкой

?. — это точный инструмент: используйте его только там, где значение слева действительно необязательно. Применять его везде подряд — ошибка, потому что это может скрывать настоящие баги.

Например, если вы опечатаетесь в имени свойства, ?. молча вернёт undefined вместо того, чтобы дать вам заметить проблему:

let user = { name: 'John' };

console.log(user?.nmae); // undefined — typo hidden, no error to alert you
Внимание

Если переменная вроде user должна всегда существовать (например, это обязательный аргумент), обращайтесь к ней напрямую: user.address?.city, а не user?.address?.city. Тогда действительно отсутствующий user всё равно выбросит ошибку как можно раньше, что поможет вам поймать баг, а не замаскировать его.

Сочетание с оператором нулевого слияния

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

javascript— editable

Здесь user?.name вычисляется в undefined, после чего ?? вступает в игру и подставляет запасное значение 'Anonymous'. Такое сочетание — один из самых распространённых практических случаев применения ?..

Поддержка браузерами

Опциональная цепочка является частью спецификации ES2020. Она поддерживается во всех современных браузерах (Chrome, Firefox, Safari и Edge) и в Node.js 14 и выше, поэтому вы можете использовать её без транспилятора в актуальных средах. Чтобы глубже разобраться в значениях, которые она проверяет, смотрите типы данных JavaScript и методы object и this.

Проверьте свои знания

Практика
Что вернёт user?.address?.city, если user равен undefined?
Что вернёт user?.address?.city, если user равен undefined?
Практика
В выражении a?.b.c, когда a существует, но b равен null, что произойдёт?
В выражении a?.b.c, когда a существует, но b равен null, что произойдёт?
Практика
Какие утверждения об операторе опциональной цепочки верны?
Какие утверждения об операторе опциональной цепочки верны?
Was this page helpful?