Learn JavaScript

?. - оператор optional chaining для безопасного доступа в JavaScript

?. - это оператор optional chaining, который позволяет безопасно читать свойства, элементы и вызывать методы.

Если на пути встречается null или undefined, выражение не падает с ошибкой, а возвращает undefined.

Это особенно полезно при работе с вложенными объектами, данными из API и значениями, которые могут отсутствовать.

Синтаксис

У optional chaining есть несколько форм.

Доступ к свойству

object?.property

Доступ по индексу или ключу

object?.[key]

Безопасный вызов

fn?.()
object.method?.()

Что делает ?.

?. проверяет левую часть только на null и undefined.

Если слева нормальное значение, доступ продолжается как обычно. Если слева null или undefined, результатом будет undefined.

const user = null;

console.log(user?.name); // undefined

Без optional chaining такой код привёл бы к ошибке.

const user = null;

// console.log(user.name); // TypeError

Безопасное чтение вложенных свойств

Это самый частый сценарий.

const user = {
  profile: {
    contacts: {
      email: "sam@example.com"
    }
  }
};

console.log(user.profile?.contacts?.email); // "sam@example.com"
console.log(user.profile?.contacts?.phone); // undefined

Если какого-то звена в цепочке нет, выполнение просто остановится и вернёт undefined.

Доступ по индексу и ключу

Optional chaining работает не только с точкой, но и со скобками.

const user = { profile: { name: "Sam" } };
const key = "name";

console.log(user.profile?.[key]); // "Sam"

Это удобно, когда ключ хранится в переменной.

Так же можно безопасно читать элементы массива:

const list = ["a", "b", "c"];

console.log(list?.[0]); // "a"
console.log(list?.[10]); // undefined

Безопасный вызов функции

?.() удобен, когда функция или метод могут отсутствовать.

const onSuccess = undefined;

console.log(onSuccess?.()); // undefined

Для метода тоже работает:

const user = {
  getName() {
    return "Sam";
  }
};

console.log(user.getName?.()); // "Sam"

Если метода нет, ошибки не будет:

const user = {};

console.log(user.getName?.()); // undefined

Если метод есть, но это не функция

Это важный нюанс.

?.() защищает только от отсутствия значения, но не превращает любое значение в функцию.

const user = {
  getName: 123
};

// user.getName?.(); // TypeError

Если свойство существует, но не является функцией, ошибка всё равно будет.

?., null и undefined

Optional chaining останавливается только на null и undefined.

Другие falsy-значения он не считает проблемой.

const data = {
  count: 0,
  title: "",
  enabled: false
};

console.log(data?.count);   // 0
console.log(data?.title);   // ""
console.log(data?.enabled); // false

Это важное отличие от старого подхода через &&.

?. и &&

Раньше для безопасного доступа часто писали так:

const name = user && user.profile && user.profile.name;

Сейчас обычно проще и понятнее так:

const name = user?.profile?.name;

Кроме того, ?. работает именно по правилу null / undefined, а не по всем falsy-значениям.

?. и ??

Очень часто optional chaining используют вместе с ??, чтобы задать значение по умолчанию.

const user = {};

const theme = user.settings?.theme ?? "light";

console.log(theme); // "light"

Если безопасный доступ вернул undefined, оператор ?? подставит значение справа.

Это читается очень естественно:

const city = user.profile?.address?.city ?? "Город не указан";

?. и короткое замыкание

Optional chaining работает с коротким замыканием: как только встретился null или undefined, дальше цепочка уже не вычисляется.

const user = null;

console.log(user?.profile?.name); // undefined

profile и name здесь уже не читаются.

Когда это полезно

?. удобно использовать, когда:

  • данные пришли из API и часть полей может отсутствовать
  • объект вложенный и не хочется писать длинные проверки через if
  • метод или колбэк может быть необязательным
  • нужно безопасно прочитать значение и при необходимости дать дефолт через ??

Пример:

function printEmail(user) {
  const email = user?.profile?.contacts?.email ?? "Email не указан";
  console.log(email);
}

Ограничения ?.

Optional chaining полезен, но он не решает вообще все проблемы.

Не работает с необъявленной переменной

// console.log(notDeclaredUser?.name); // ReferenceError

Если сама переменная не объявлена, ошибка будет раньше, чем optional chaining сможет сработать.

Нельзя использовать для присваивания

// user?.profile = {}; // SyntaxError

?. подходит для чтения и вызова, но не для записи.

Нельзя использовать после new в таком виде

// new User?.(); // SyntaxError

Не заменяет проверку типа

const user = {
  getName: 123
};

// user.getName?.(); // TypeError

Если значение есть, но оно неподходящего типа, optional chaining не спасёт.

Частые ошибки

Думать, что ?. проверяет все falsy-значения

const data = { count: 0 };

console.log(data?.count); // 0

Оператор реагирует только на null и undefined.

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

// user?.profile.name = "Sam"; // SyntaxError

Для записи сначала нужно убедиться, что путь существует, или создать его вручную.

Забывать про ?. перед вызовом метода

const user = {};

// user.getName();   // TypeError
console.log(user.getName?.()); // undefined

Если метод необязательный, безопасный вызов часто удобнее.

Думать, что ?. работает с необъявленной переменной

// notDeclaredUser?.name; // ReferenceError

Оператор помогает только после того, как значение уже существует как переменная.

Заменять ?. на && без учёта разницы

const data = { count: 0 };

const a = data && data.count;
const b = data?.count;

console.log(a); // 0
console.log(b); // 0

В простом случае результат может совпасть, но по смыслу и правилам работы это разные конструкции. Для безопасного доступа в современном коде ?. обычно читается лучше.

Быстрая памятка

  • ?. позволяет безопасно читать свойства, элементы и вызывать методы
  • если на пути встречается null или undefined, результатом будет undefined
  • оператор не реагирует на 0, false и пустую строку
  • удобно сочетать ?. с ??
  • ?. не работает для записи
  • ?. не спасает, если переменная вообще не объявлена
  • ?.() не поможет, если свойство существует, но не является функцией

Коротко

?. - это оператор безопасного доступа в JavaScript. Главное, что стоит запомнить: он позволяет аккуратно читать вложенные свойства и вызывать необязательные методы без TypeError, если на пути встречается null или undefined, но не заменяет проверку типов, не работает для записи и часто используется в паре с ??.