Копирование и слияние объектов

Копирование и слияние объектов

Технический фундамент копирования

В JS есть принципиальная разница между:

  • копией ссылки (const b = a);
  • поверхностной копией ({ ...a }, Object.assign).
const a = { x: 1 };
const byRef = a;
const byCopy = { ...a };

byRef.x = 2;
console.log(a.x); // 2
console.log(byCopy.x); // 1

Ключевой момент: при работе со state и конфигами почти всегда нужна копия объекта, а не вторая ссылка.

Ошибка новичка: присваивание вместо копии

const original = { name: 'Ann', age: 20 };
const copy = original;

copy.age = 21;
console.log(original.age); // 21

Проверь себя: почему изменился original, хотя мы меняли copy?

Поверхностная копия: spread

const user = { name: 'Max', age: 25 };
const cloned = { ...user };

cloned.age = 26;
console.log(user.age); // 25

Object.assign

const base = { theme: 'light' };
const result = Object.assign({}, base, { lang: 'ru' });

console.log(result); // { theme: 'light', lang: 'ru' }

Object.assign и spread делают поверхностную копию.

Смотри, что важно: Object.assign(target, ...) мутирует target и возвращает его же. Поэтому для копии обычно передают пустой объект {} как target.

Слияние объектов

const defaults = { page: 1, pageSize: 20 };
const query = { pageSize: 50, search: 'js' };

const merged = { ...defaults, ...query };
console.log(merged); // { page: 1, pageSize: 50, search: 'js' }

Ключевой момент: при одинаковых ключах побеждает объект справа.

Важный edge case: вложенные объекты

const a = { profile: { city: 'Minsk' } };
const b = { ...a };

b.profile.city = 'Warsaw';
console.log(a.profile.city); // Warsaw

Это происходит из-за поверхностного копирования.

Микро-сценарии

  1. Обновление настроек пользователя без мутации исходника.
  2. Сбор итогового конфига из default + пользовательских параметров.

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

  • Путать «копию объекта» и «копию ссылки».
  • Не учитывать вложенные структуры.
  • Неверно рассчитывать приоритет при слиянии.
  • Мутировать входящие данные функции.

Краткий итог

  • Присваивание не копирует объект, а копирует ссылку.
  • Spread и Object.assign создают поверхностную копию.
  • Слияние удобно делать через { ...a, ...b }.
  • При конфликте ключей побеждает правый объект.
  • Для вложенных структур нужна отдельная стратегия deep copy.

Например:

  • structuredClone(obj) (в современных окружениях) — делает deep copy для многих типов;
  • JSON.parse(JSON.stringify(obj)) — простая, но «ломкая» техника (теряются undefined, функции, Date, Map/Set, BigInt и т.д.).