JavaScript / 39 минут

Объекты и деструктуризация

Читайте свойства, копируйте объекты и применяйте optional chaining.

В процессе Теория: впереди Практика: впереди Тест: впереди
09 / 22
После этой главы
  • Читайте свойства, копируйте объекты и применяйте optional chaining.
  • Применить тему в задаче «Объекты и деструктуризация» и объяснить граничные случаи.

Около 9 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Объект как набор свойств и идентичность

Объект объединяет связанные значения: профиль пользователя, параметры запроса или состояние урока. У каждого свойства есть ключ и значение. В обычном литерале ключ часто выглядит как имя переменной, но фактически имя свойства не создаёт отдельную локальную переменную. Чтение user.name означает получение свойства name у значения user; имя user сначала должно разрешиться в текущей области видимости.

Объекты имеют идентичность. Если присвоить один объект двум переменным, обе связи будут указывать на него. Изменение свойства через одну связь заметно через другую. При этом присваивание переменной нового объекта меняет только эту связь. Такое различие важно для параметров функций, общего состояния и сравнения результатов.

JavaScript
const profile = { name: 'Анна', completed: 2 };
const alias = profile;
alias.completed = 3;
console.log(profile.completed); // 3
console.log(alias === profile); // true

Доступ через точку и вычисляемый ключ

Точечная запись удобна, когда имя свойства известно в исходном коде. Квадратные скобки нужны для вычисляемого ключа: например, пользователь выбрал столбец таблицы, и его имя хранится в переменной. В выражении object[key] сначала вычисляется key, а затем читается соответствующее свойство. Запись object.key, напротив, ищет буквально свойство с именем key.

Чтение отсутствующего свойства обычно даёт undefined. Но попытка прочитать свойство у null или undefined бросает TypeError. Это разные ситуации: отсутствие поля у существующего объекта и отсутствие самого объекта. Не смешивайте их в одной неясной проверке. При необходимости проверить именно собственное свойство используйте Object.hasOwn, учитывая, что цепочка прототипов может содержать свойства вне данных объекта.

JavaScript
const lesson = { title: 'Объекты', minutes: 35 };
const key = 'title';
console.log(lesson[key]); // Объекты
console.log(lesson.key); // undefined
console.log(Object.hasOwn(lesson, 'minutes')); // true

Optional chaining и запасное значение

Optional chaining ?. прекращает доступ, если промежуточное значение равно null или undefined, и возвращает undefined вместо исключения. Он полезен, когда отсутствие части структуры допустимо по контракту. Если отсутствие означает ошибку схемы, бесконечные ?. могут скрыть проблему: программа начнёт молча показывать пустые данные вместо диагностики неправильного ответа сервера.

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

JavaScript
function displayName(user) {
  return user?.name ?? 'Гость';
}
console.log(displayName(null)); // Гость
console.log(displayName({ name: '' })); // пустая строка

Деструктуризация и значения по умолчанию

Деструктуризация создаёт локальные связи из свойств объекта. Можно переименовать поле, задать значение по умолчанию и собрать оставшиеся свойства через rest. Значение по умолчанию применяется для undefined, но не для null. Поэтому {name = 'Гость'} и name ?? 'Гость' не всегда эквивалентны. Перед выбором синтаксиса уточните, какие варианты отсутствия допускаются.

JavaScript
const options = { limit: 20, theme: 'dark' };
const { limit: pageSize, theme = 'light' } = options;
console.log(pageSize, theme); // 20 dark

Деструктуризация параметра сама по себе не проверяет вход. Если функция ожидает объект, а получает null, извлечение полей может бросить ошибку ещё до выполнения тела. Для внешнего JSON сначала нужна runtime-проверка. Красивое разложение объекта не заменяет валидацию его структуры и типов значений.

Копирование, перечисление и вложенность

Spread создаёт новый внешний объект с собственными перечислимыми свойствами. Порядок имеет значение: более поздняя запись свойства заменяет более раннюю. Это удобно для обновления настроек, но опасно при бездумном копировании внешнего body в запись пользователя. Разрешённые поля нужно выбирать явно. Приватные данные не должны случайно попасть в публичный ответ через spread.

JavaScript
const oldState = { filter: 'all', paging: { page: 1 } };
const newState = { ...oldState, filter: 'done' };
console.log(newState.paging === oldState.paging); // true

Копия поверхностная: вложенный paging остался тем же объектом. Для независимого обновления его копируют отдельно. Object.keys возвращает собственные строковые ключи, Object.values — значения, Object.entries — пары. Эти операции помогают обрабатывать словари без случайного обхода унаследованных свойств через for...in.

Практика и границы контракта

Нужно вернуть имя либо гостевую подпись для null, отсутствующего поля и null в самом поле. Пустую строку требуется сохранить. Составьте таблицу этих четырёх ситуаций до написания выражения. Затем сравните результаты возможных решений через &&, ||, ?. и ??. Это покажет, где выбранная конструкция делает больше преобразований, чем требует условие.

Не меняйте переданный объект ради удобства: добавление user.name = 'Гость' нарушает разделение чтения и обновления. Возвращайте вычисленное строковое значение. Для самостоятельного продолжения добавьте вложенный профиль и отдельно решите, какие уровни структуры могут отсутствовать, а какие должны считаться ошибкой входных данных.

Загружаем редактор…
Следующая темаMap и Set

Музыка: Reverie — Scott Buckley · CC BY 4.0