?. - это оператор 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, но не заменяет проверку типов, не работает для записи и часто используется в паре с ??.