Learn JavaScript

Вложенные объекты и безопасный доступ

В реальных данных объекты часто вложены друг в друга: настройки, профили, ответы 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 и "".
  • Для записи во вложенность сначала создаются промежуточные объекты.