В реальных данных объекты часто вложены друг в друга: настройки, профили, ответы API. Главная проблема вложенности - безопасно обращаться к свойствам, которые могут отсутствовать.
1) Как выглядит вложенный объект
const user = {
id: 1,
profile: {
name: "Sam",
contacts: {
email: "sam@example.com"
}
}
};
console.log(user.profile.name); // "Sam"
console.log(user.profile.contacts.email); // "sam@example.com"
Чтение идет по цепочке через точку или скобки.
2) Типичная ошибка: TypeError при отсутствии промежуточного объекта
Если какого-то звена цепочки нет, попытка прочитать вложенное свойство приводит к ошибке.
const user = { id: 1 };
try {
console.log(user.profile.name);
} catch (e) {
console.log(e.name); // "TypeError"
}
Причина простая: user.profile равен undefined, а у undefined нет свойства name.
3) Классическая защита: проверки через if
Проверки можно делать вручную - от внешнего к внутреннему.
function getUserName(user) {
if (!user) return null;
if (!user.profile) return null;
if (!user.profile.name) return null;
return user.profile.name;
}
console.log(getUserName({ profile: { name: "Sam" } })); // "Sam"
console.log(getUserName({})); // null
Это работает, но быстро становится громоздким.
4) Optional chaining: ?. для безопасного чтения
Optional chaining (?.) позволяет читать свойство, даже если часть цепочки отсутствует. Если на пути встречается null или undefined, результатом будет undefined, без ошибки.
const user = { id: 1 };
const name = user.profile?.name;
console.log(name); // undefined
Пример с глубокой вложенностью:
const user = { profile: { contacts: { email: "sam@example.com" } } };
console.log(user.profile?.contacts?.email); // "sam@example.com"
console.log(user.profile?.contacts?.phone); // undefined
Optional chaining со скобками
Удобно, когда ключ в переменной:
const user = { profile: { name: "Sam" } };
const key = "name";
console.log(user.profile?.[key]); // "Sam"
5) Значение по умолчанию: ?? вместо ||
Когда безопасный доступ дает undefined, часто нужно значение по умолчанию. Для этого используют nullish coalescing (??).
const user = {};
const theme = user.settings?.theme ?? "light";
console.log(theme); // "light"
?? срабатывает только для null и undefined.
Это отличается от ||, который срабатывает для любых falsy значений, включая 0 и пустую строку.
const data = { retries: 0 };
const a = data.retries || 3;
const b = data.retries ?? 3;
console.log(a); // 3
console.log(b); // 0
Если 0 - корректное значение, обычно лучше использовать ??.
6) Безопасная запись: сначала создать путь
Optional chaining помогает читать, но не решает запись. Для записи по вложенному пути нужно гарантировать, что промежуточные объекты существуют.
Пример: нужно записать settings.theme.
const user = {};
if (!user.settings) user.settings = {};
user.settings.theme = "dark";
console.log(user); // { settings: { theme: "dark" } }
Если нужно делать так часто, путь обычно выносят в функцию или аккуратно строят структуру заранее.
7) Частые ошибки
1) Пытаться заменить ?. на && без понимания результата
Иногда используют цепочки &&, но они возвращают одно из значений, а не обязательно boolean. Это может запутывать.
const user = { profile: { name: "Sam" } };
const name = user && user.profile && user.profile.name;
console.log(name); // "Sam"
Такой стиль рабочий, но ?. читается проще:
const user = { profile: { name: "Sam" } };
const name = user.profile?.name;
console.log(name); // "Sam"
2) Использовать || для значений по умолчанию там, где важны 0 и ""
Если значение может быть 0 или пустой строкой, || часто дает неверный “дефолт”. В таких местах лучше ??.
Что стоит запомнить
- Вложенные объекты часто содержат отсутствующие ветки, поэтому прямой доступ может привести к TypeError.
?.безопасно читает свойства: если на путиnull/undefined, вернетсяundefined.??задает значение по умолчанию только дляnull/undefinedи не ломает0и"".- Для записи во вложенность сначала создаются промежуточные объекты.