Skip to Content
JavaScriptОбъекты и копии

Объекты, копии и коллекции

Уровень: middle.

Вопрос

Почему копия объекта меняет оригинал, и когда объект хуже Map?

Короткий ответ

Объект и массив передаются по ссылке. const b = a не копирует поля: это тот же объект. Присваивание поля в b видно через a.

Поверхностная копия — { ...obj }, Object.assign, array.slice() и [...array]. Вложенный объект остаётся общей ссылкой. Глубокая копия: structuredClone для обычных данных. Она не клонирует функции, DOM-узлы и некоторые классы. JSON.parse(JSON.stringify(x)) теряет undefined, Date превращает в строку, выкидывает циклы.

Map хранит любые ключи, в том числе объекты, и помнит размер. Ключи обычного объекта — строки и символы. Число как ключ станет строкой "1". Set хранит уникальные значения. Для него уникальность объектов — по ссылке, не по полям.

Разбор

Сравнение {} и {} через === ложно: это разные ссылки. Примитивы сравниваются по значению.

Немутабельное обновление в интерфейсе — новая ссылка на изменённый уровень и переиспользование остального:

const next = { ...user, address: { ...user.address, city } }

Так реактивность и React-подобное сравнение по ссылке видят изменение. Глубокая мутация вложенного поля без новой ссылки легко теряется, если слой смотрит только на верхний объект. Во Vue reactive как раз отслеживает вложенную запись, но привычка копировать уровень полезна на границе стора и кэша.

Object.keys не видит символы и неперечисляемые поля. Порядок строковых ключей у обычных объектов сохраняется для строковых ключей без целочисленного вида. Целочисленные ключи сортируются как индексы.

На что смотрят на senior

structuredClone использует алгоритм структурированного клонирования, тот же класс задач, что postMessage. Для состояния экрана чаще хватает поверхностного обновления одного уровня. Глубокий клон всего стора на каждый ввод дорог и рвёт ссылки, по которым подписан кэш.

Ключ Map из объекта жив, пока на объект есть ссылка. Если ключом положили DOM-узел и забыли map.delete, коллекция держит узел. Для кэша с вытеснением ближе WeakMap: ключ не мешает сборке мусора, итерировать такой словарь нельзя.

Типичные ошибки

  • Считать spread глубокой копией.
  • Класть в объект ключи-объекты и ждать, что это Map.
  • Сравнивать объекты через == и ждать сравнение полей.
  • Клонировать через JSON данные с датами и undefined.

Задача

Что изменится в user после этих двух строк?

const user = { city: 'Kazan', address: { street: 'A' } } const copy = { ...user, city: 'Moscow' } copy.address.street = 'B'
Показать ответ

city у user останется 'Kazan': верхний уровень скопирован, поле перезаписано в новом объекте.

street станет 'B' и у оригинала. Spread поверхностный, address — та же ссылка.

Last updated on