Объекты и ссылки

объекты≈ 7 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Объект — главная структура данных JavaScript: в объектах живут настройки, пользователи, ответы серверов. А ещё это первая встреча со ссылками — механизмом, из-за которого «копия» объекта иногда оказывается вовсе не копией.

Разделы темы:·

Объект — это набор пар «ключ: значение». Если обычная переменная — коробка с одной этикеткой, то объект — шкаф с подписанными ящиками: в каждом лежит своё значение, и доставать их можно по имени.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Запись в фигурных скобках называется литералом объекта. Свойства перечисляются через запятую, а читаются через точку: user.name — «у объекта user возьми свойство name». Если свойства нет, ошибки не будет — вернётся undefined:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Точка удобна, но капризна: она работает, только если имя свойства известно заранее и выглядит как обычный идентификатор — без пробелов и дефисов. Как только имя лежит в переменной, точка ломается:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Внутри квадратных скобок может стоять любое выражение: переменная, строка, даже вызов функции. И только скобки справятся с именем, в котором есть пробел:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
ЗаписьКогда использовать
user.nameимя известно заранее и без пробелов
user[key]имя свойства лежит в переменной
user["имя с пробелом"]имя не похоже на идентификатор

Объект не застывает при создании — это живая структура. Присвоили значение несуществующему свойству — оно появилось. Применили оператор delete — исчезло:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Ещё одна деталь: обычный ключ объекта — строка. Даже если написать в скобках число, JavaScript тихо превратит его в строку — и обращаться к такому свойству можно обоими способами:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Теперь главное. Примитив лежит в переменной целиком: присваивание копирует само значение, и дальше копия живёт своей жизнью:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

С объектами всё иначе. Переменная хранит не объект, а ссылку — адрес места в памяти, где объект лежит. Присваивание копирует адрес, и обе переменные начинают указывать на одно и то же:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

= копирует пульт, а не телевизор

Для объектов операторы == и === сравнивают не содержимое, а ссылки: равны только две переменные, указывающие на один и тот же объект.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Раз в переменной лежит ссылка, становится понятно, почему объект в const можно менять. Константа охраняет только саму ссылку — содержимого объекта она не касается:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Запрещено другое: user = { name: "Вера" } — попытка записать в константу новую ссылку закончится ошибкой. А когда нужна настоящая независимая копия, объект клонируют — проще всего spread-синтаксисом:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

То же самое умеет Object.assign({}, user) — более старая запись, которая заодно сливает несколько объектов в один. В современном коде чаще встречается spread: короче и нагляднее.

И spread, и Object.assign копируют только верхний «этаж» объекта. Если в свойстве лежит другой объект, скопируется его ссылка — и вложенная часть останется общей:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Полную копию со всеми уровнями делает встроенная функция structuredClone(user). Чем поверхностная копия отличается от глубокой и что при копировании теряется, разбирает статья «Копирование объектов: поверхностное и глубокое».

  • Объект создаётся литералом {} и хранит пары ключ–значение; свойство читается через точку: user.name.
  • Квадратные скобки user[key] нужны, когда имя свойства лежит в переменной или содержит пробел.
  • Свойства добавляются простым присваиванием и удаляются оператором delete; обычный ключ — строка, и число в скобках тоже становится строкой.
  • Примитивы копируются по значению, объекты — по ссылке: после b = a обе переменные меняют один и тот же объект.
  • const запрещает менять ссылку, но не содержимое; независимую копию делают { ...obj } или Object.assign — они копируют один уровень.

1/3Что выведет код: const a = { n: 1 }; const b = a; b.n = 2; console.log(a.n);

2/3Что выведет console.log({} == {})?

3/3Объявлено const user = { age: 25 }. Какая строка выполнится без ошибки?

Почини скидку

Программист хотел сделать копию тарифа и снизить в ней цену, но скидка затёрла и оригинал. Исправьте одну строку, чтобы вывод совпал с комментариями.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

const sale = { ...base }; — spread создаёт новый объект и переносит в него свойства, поэтому sale.price = 80 меняет копию, а не оригинал. Подойдёт и Object.assign({}, base).

Ключ в переменной

Напишите функцию read(obj, key), которая возвращает значение свойства объекта obj с именем, лежащим в переменной key.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

return obj[key]; — квадратные скобки подставляют значение переменной. Точка не подойдёт: obj.key искала бы свойство с буквальным именем key.