Перейти к содержимому
Теория · Объекты · урок 7 из 13

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

Представьте картотеку. Анкета лежит в ящике, а в переменной записан только номер этого ящика. Если переписать номер на второй листок, анкет не станет две: оба листка ведут к одному ящику.

Присваивание объекта другой переменной работает так же. Второй объект не появляется, и изменение через новое имя видно через старое:

JavaScript
const settings = { size: "M", color: "black" };
const other = settings;

other.size = "L";
console.log(settings.size); // L
console.log(other === settings); // true

Давайте разберём пример по строкам:

  1. const other = settings копирует не объект, а номер ящика.
  2. other.size = "L" меняет анкету в этом ящике.
  3. settings.size читает ту же анкету и печатает L.
  4. other === settings даёт true: оба имени ведут к одному объекту.

Как и с массивами, переменная хранит не сам объект, а ссылку на него. Оператор === на объектах проверяет, один ли это объект, а не одинаковы ли свойства. Поэтому { a: 1 } === { a: 1 } даёт false: это два разных объекта.

Синтаксис расширения и Object.assign

Чтобы получить вторую анкету, нужна ксерокопия: новый объект с теми же свойствами. Для этого есть два способа.

Три точки

Синтаксис расширения { ...settings } создаёт новый объект с теми же свойствами. Если ключ встречается дважды, остаётся значение, записанное позже. Поэтому:

  • { ...settings, size: "L" } — копия с другим размером;
  • в { size: "L", ...settings } размер "L" заменится скопированным "M".

Object.assign

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

JavaScript
const settings = { size: "M", color: "black" };
const a = { ...settings };
const b = Object.assign({}, settings);
const c = { ...settings, size: "L" };

a.color = "red";
console.log(a === settings, settings.color); // false black
console.log(b); // { size: 'M', color: 'black' }
console.log(c); // { size: 'L', color: 'black' }

Разберём по строкам:

  1. a — копия через три точки, b — через Object.assign с пустым {}.
  2. c — копия, в которой размер сразу заменён на "L".
  3. a.color = "red" меняет только копию: a === settings даёт false, а settings.color остаётся black.
  4. b и c печатаются как отдельные объекты со своими свойствами.

Вложенные объекты

Синтаксис расширения и Object.assign копируют только свойства верхнего уровня. Если значение свойства — объект, в копию попадает ссылка на него, и вложенный объект остаётся общим. Такая копия называется поверхностной.

В картотеке это выглядит так. В строке «Адрес» анкеты записан не сам адрес, а номер карточки с адресом. Ксерокопия переписывает этот номер, и обе анкеты ведут к одной карточке.

Функция structuredClone копирует все уровни и даёт глубокую копию: она переписывает и саму карточку с адресом.

JavaScript
const user = { name: "Ann", address: { city: "Oslo" } };
const shallow = { ...user };
const deep = structuredClone(user);

user.address.city = "Rome";
console.log(shallow.address.city); // Rome
console.log(deep.address.city); // Oslo

Изменение user.address.city видно через shallow, потому что оба объекта хранят ссылку на один и тот же адрес. deep получил собственный адрес, и его город остался прежним.

Пройдите программу по шагам и следите за city в трёх объектах: после замены города он меняется у user и shallow сразу, а у deep — нет.

ВживуюПоверхностная и глубокая копияшаг 1 из 8 · рамок: 1

user — объект с вложенным объектом address.

Следите за city в трёх объектах

Здесь аналогия с ксерокопией перестаёт работать. Бумажная копия никогда не меняется вслед за оригиналом. А у поверхностной копии меняется всё, что лежит во вложенных объектах, потому что они общие.

Разницу между поверхностной и глубокой копией часто спрашивают на собеседовании. Короткий ответ: три точки и Object.assign копируют один уровень, а structuredClone — все.

Что выбрать

Несколько правил, которые помогают выбрать способ:

  • structuredClone копирует и массивы, и объекты внутри массивов;
  • массив внутри объекта ведёт себя как вложенный объект: синтаксис расширения копирует ссылку на него, а не элементы;
  • поверхностной копии хватает, когда значения свойств — числа, строки и другие примитивы;
  • structuredClone не может скопировать метод: на объекте с методом программа останавливается с ошибкой DataCloneError.

Частая ошибка

Первым аргументом Object.assign передали объект, который нужно сохранить:

JavaScript
const defaults = { size: "M", color: "black" };
const mine = Object.assign(defaults, { size: "L" }); // ✗

console.log(mine.size);

Программа выводит L, но defaults.size теперь тоже "L", и mine === defaults. Object.assign меняет свой первый аргумент и возвращает его же.

Новый объект получится, если первым передать {}: Object.assign({}, defaults, { size: "L" }). Ещё один способ — запись { ...defaults, size: "L" }.

Главное

  • Присваивание копирует ссылку: оба имени ведут к одному объекту, а === проверяет, один ли это объект.
  • { ...obj } и Object.assign({}, obj) дают поверхностную копию: вложенные объекты остаются общими.
  • structuredClone(obj) даёт глубокую копию, но не копирует методы.
  • Object.assign меняет свой первый аргумент, поэтому для копии первым передают {}.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом