Deep clone объекта

Создание полной независимой копии объекта — изменения в клоне не влияют на оригинал.

Задача

Нужно скопировать вложенный объект или массив так, чтобы изменения в копии не затрагивали исходный объект. Обычный spread { ...obj } копирует только первый уровень.

Решение

Способ 1 — structuredClone (современный, рекомендуемый)

const original = {
  name: 'Alice',
  address: { city: 'Moscow', zip: '101000' },
  tags: ['js', 'css'],
};

const clone = structuredClone(original);

clone.address.city = 'SPb';
clone.tags.push('html');

console.log(original.address.city); // 'Moscow' — не изменился
console.log(original.tags);         // ['js', 'css'] — не изменился

structuredClone поддерживает Date, Map, Set, ArrayBuffer, но не клонирует функции и undefined-поля.

Способ 2 — JSON.parse(JSON.stringify(obj)) (простой фоллбэк)

const clone = JSON.parse(JSON.stringify(original));

Ограничения: теряет Date (превращает в строку), undefined, функции, Symbol, Map, Set, циклические ссылки бросают ошибку.

Способ 3 — рекурсивная функция (полный контроль)

function deepClone(value) {
  if (value === null || typeof value !== 'object') return value;
  if (value instanceof Date) return new Date(value);
  if (Array.isArray(value)) return value.map(deepClone);

  return Object.fromEntries(
    Object.entries(value).map(([k, v]) => [k, deepClone(v)])
  );
}

Ключевые моменты

  • structuredClone — лучший выбор в 2024+, поддерживается во всех современных браузерах и Node 17+.
  • JSON.parse/stringify — быстро, но теряет типы; используй только для простых JSON-данных.
  • { ...obj } и Object.assignповерхностные копии, вложенные объекты остаются общими.
  • Циклические ссылки: structuredClone обрабатывает, JSON.stringify — бросает TypeError.

Варианты

  • Lodash _.cloneDeep(obj) — надёжный, обрабатывает все edge cases; добавляет зависимость.
  • Immer — для иммутабельного обновления вложенных объектов в стейте.

Когда НЕ использовать

  • Если нужно просто обновить поля первого уровня — достаточно spread: { ...obj, name: 'Bob' }.
  • Для клонирования классов с методами — structuredClone не копирует prototype, нужна ручная реализация.

Связанные рецепты / темы