Объекты и ссылки
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Объект — главная структура данных JavaScript: в объектах живут настройки, пользователи, ответы серверов. А ещё это первая встреча со ссылками — механизмом, из-за которого «копия» объекта иногда оказывается вовсе не копией.
Объект — это набор пар «ключ: значение». Если обычная переменная — коробка с одной этикеткой, то объект — шкаф с подписанными ящиками: в каждом лежит своё значение, и доставать их можно по имени.
Запись в фигурных скобках называется литералом объекта. Свойства перечисляются через запятую, а читаются через точку: user.name — «у объекта user возьми свойство name». Если свойства нет, ошибки не будет — вернётся undefined:
Точка удобна, но капризна: она работает, только если имя свойства известно заранее и выглядит как обычный идентификатор — без пробелов и дефисов. Как только имя лежит в переменной, точка ломается:
Внутри квадратных скобок может стоять любое выражение: переменная, строка, даже вызов функции. И только скобки справятся с именем, в котором есть пробел:
| Запись | Когда использовать |
|---|---|
user.name | имя известно заранее и без пробелов |
user[key] | имя свойства лежит в переменной |
user["имя с пробелом"] | имя не похоже на идентификатор |
Объект не застывает при создании — это живая структура. Присвоили значение несуществующему свойству — оно появилось. Применили оператор delete — исчезло:
Ещё одна деталь: обычный ключ объекта — строка. Даже если написать в скобках число, JavaScript тихо превратит его в строку — и обращаться к такому свойству можно обоими способами:
Теперь главное. Примитив лежит в переменной целиком: присваивание копирует само значение, и дальше копия живёт своей жизнью:
С объектами всё иначе. Переменная хранит не объект, а ссылку — адрес места в памяти, где объект лежит. Присваивание копирует адрес, и обе переменные начинают указывать на одно и то же:
= копирует пульт, а не телевизор
Для объектов операторы == и === сравнивают не содержимое, а ссылки: равны только две переменные, указывающие на один и тот же объект.
Раз в переменной лежит ссылка, становится понятно, почему объект в const можно менять. Константа охраняет только саму ссылку — содержимого объекта она не касается:
Запрещено другое: user = { name: "Вера" } — попытка записать в константу новую ссылку закончится ошибкой. А когда нужна настоящая независимая копия, объект клонируют — проще всего spread-синтаксисом:
То же самое умеет Object.assign({}, user) — более старая запись, которая заодно сливает несколько объектов в один. В современном коде чаще встречается spread: короче и нагляднее.
И spread, и Object.assign копируют только верхний «этаж» объекта. Если в свойстве лежит другой объект, скопируется его ссылка — и вложенная часть останется общей:
Полную копию со всеми уровнями делает встроенная функция 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 }. Какая строка выполнится без ошибки?
Почини скидку
Программист хотел сделать копию тарифа и снизить в ней цену, но скидка затёрла и оригинал. Исправьте одну строку, чтобы вывод совпал с комментариями.
const sale = { ...base }; — spread создаёт новый объект и переносит в него свойства, поэтому sale.price = 80 меняет копию, а не оригинал. Подойдёт и Object.assign({}, base).
Ключ в переменной
Напишите функцию read(obj, key), которая возвращает значение свойства объекта obj с именем, лежащим в переменной key.
return obj[key]; — квадратные скобки подставляют значение переменной. Точка не подойдёт: obj.key искала бы свойство с буквальным именем key.