Копирование объектов в JavaScript — одна из тех тем, которые поначалу кажутся простыми, а потом преподносят неожиданные сюрпризы.

Присвоить объект через = — не копирование. Это просто ещё одна ссылка на тот же объект. Измените копию — изменится оригинал:

const original = { name: 'Иван', scores: [10, 20, 30] };
const copy = original;

copy.name = 'Пётр';
console.log(original.name); // 'Пётр' — изменился оригинал!

Для поверхностного копирования используют Object.assign() или spread-оператор {...obj}. Но они копируют только первый уровень. Вложенные объекты и массивы по-прежнему остаются общими.

В 2022 году JavaScript получил structuredClone() — стандартный способ сделать полную глубокую копию.

Попробуйте сами

Нажмите на кнопки и посмотрите, как ведут себя разные способы копирования:

Нажмите кнопку, чтобы увидеть, что произойдёт после изменения «копии»

Как пользоваться

structuredClone() принимает значение и возвращает его полную независимую копию:

const original = {
  name: 'Иван',
  address: { city: 'Москва' },
  scores: [10, 20, 30],
};

const copy = structuredClone(original);

copy.name = 'Пётр';
copy.address.city = 'Питер';
copy.scores.push(40);

console.log(original.name);         // 'Иван'    — не изменился
console.log(original.address.city); // 'Москва'  — не изменился
console.log(original.scores);       // [10, 20, 30] — не изменился

Все вложенные объекты и массивы скопированы по значению, а не по ссылке.

Почему не JSON.parse(JSON.stringify())

До появления structuredClone() глубокое копирование часто делали через JSON.parse(JSON.stringify(obj)). Этот способ работает, но ломается на нескольких типах данных:

const obj = {
  date: new Date(),       // станет строкой
  fn: () => {},          // исчезнет
  undef: undefined,      // исчезнет
  map: new Map(),        // станет пустым объектом
};

// JSON-способ теряет данные
const broken = JSON.parse(JSON.stringify(obj));
console.log(broken.date); // строка, не Date

// structuredClone корректно копирует Date, Map, Set и другие типы
const correct = structuredClone(obj);
console.log(correct.date instanceof Date); // true

structuredClone() корректно копирует Date, Map, Set, ArrayBuffer, RegExp и другие встроенные типы.

Что нельзя клонировать

Функции клонировать нельзя — structuredClone() выбросит ошибку. Это логично: функция — это поведение, а не данные. DOM-узлы тоже не поддерживаются.

structuredClone({ fn: () => {} }); // TypeError

Поддержка в браузерах

structuredClone() поддерживается в Chrome 98+, Firefox 94+, Safari 15.4+, а также в Node.js начиная с версии 17. Актуально на caniuse.com.

Что запомнить

structuredClone() — стандартный способ сделать глубокую копию объекта. Он лучше JSON.parse(JSON.stringify()), потому что корректно обрабатывает Date, Map, Set и другие встроенные типы. Функции и DOM-узлы клонировать нельзя.

Если встречаете ошибку TypeError при работе с объектами — загляните в статью про TypeError: что это и как исправить. А для знакомства с типами данных в целом — типы данных в JavaScript.


«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

ТелеграмПодкастБесплатные учебники