Skip to main content

Глибока копія об'єкта

Щоб зробити глибоку копію об'єкта, використовують вбудований structuredClone(obj), а для складних проєктів _.cloneDeep(obj) з lodash. Глибока копія, це повністю незалежний об'єкт, включно з усіма вкладеними об'єктами й масивами, тому зміни в копії не впливають на оригінал.

Теорія

TL;DR

  • Глибока копія відтворює все дерево об'єкта, а не лише верхній рівень.
  • structuredClone(obj), вбудований, сучасний і швидкий, але не копіює функції та прототипи.
  • JSON.parse(JSON.stringify(obj)) працює скрізь, але мовчки втрачає багато типів.
  • _.cloneDeep(obj) з lodash, найнадійніший варіант для складних структур.
  • Власна рекурсивна функція корисна для розуміння механіки, але не покриває Map, Set, Date, RegExp.
  • Практичне правило: structuredClone() за замовчуванням, cloneDeep(), коли потрібна максимальна сумісність.

Швидкий приклад

javascript
const user = { name: 'Alice', address: { city: 'Kyiv', zip: 12345 }, tags: ['dev', 'frontend'] }; const clone = structuredClone(user); clone.address.city = 'Lviv'; clone.tags.push('react'); console.log(user.address.city); // "Kyiv" console.log(user.tags); // ['dev', 'frontend']

Що таке «глибока копія»

Глибока копія (deep copy), це коли створюється повністю незалежний об'єкт, включно з усіма вкладеними об'єктами та масивами. Зміни в копії не впливають на оригінал.

Протилежність, це поверхнева копія ({ ...obj }, Object.assign()), яка копіює лише верхній рівень і залишає вкладені об'єкти спільними посиланнями.

Спосіб 1: сучасний, structuredClone()

Найнадійніший і найпростіший метод, доступний в усіх сучасних браузерах і в Node.js 17 та новіших.

javascript
const user = { name: 'Alice', address: { city: 'Kyiv', zip: 12345 }, tags: ['dev', 'frontend'] }; const clone = structuredClone(user); clone.address.city = 'Lviv'; clone.tags.push('react'); console.log(user.address.city); // "Kyiv" console.log(user.tags); // ['dev', 'frontend']

Переваги:

  • копіює об'єкти, масиви, дати, Map, Set та інші структуровані типи;
  • безпечний і швидкий, бо реалізований у рушії.

Обмеження:

  • не копіює функції та прототипи (вони втрачаються);
  • не підтримує undefined як ключ (але підтримує як значення).

Спосіб 2: через JSON.parse(JSON.stringify())

Класичний спосіб, який працює скрізь, навіть у старих браузерах:

javascript
const user = { name: 'Alice', address: { city: 'Kyiv' }, tags: ['frontend', 'dev'] }; const clone = JSON.parse(JSON.stringify(user)); clone.address.city = 'Lviv'; console.log(user.address.city); // "Kyiv"

Переваги:

  • просто й універсально, без залежностей.

Недоліки:

  • втрачає:
    • функції;
    • undefined, Symbol, BigInt;
    • Date, Map, Set, RegExp (перетворюються на звичайні об'єкти або рядки).
  • повільніший на дуже великих об'єктах.

Спосіб 3: за допомогою lodash (_.cloneDeep())

Якщо проєкт великий або використовується TypeScript, це найнадійніший варіант.

javascript
import _ from 'lodash'; const clone = _.cloneDeep(user);

Переваги:

  • глибоко копіює будь-які типи даних (включно з Date, Map, Set, Buffer тощо);
  • працює стабільно в усіх середовищах.

Недолік:

  • потребує встановлення бібліотеки:
bash
npm install lodash

Спосіб 4: ручна рекурсивна копія (для розуміння)

Якщо хочете зрозуміти механіку, ось базова реалізація:

javascript
function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; if (Array.isArray(obj)) { return obj.map(deepClone); } const clone = {}; for (const key in obj) { clone[key] = deepClone(obj[key]); } return clone; } const user = { name: 'Alice', address: { city: 'Kyiv' } }; const clone = deepClone(user); clone.address.city = 'Lviv'; console.log(user.address.city); // "Kyiv"

Працює для звичайних об'єктів і масивів, але не для Map, Set, Date, RegExp тощо: для цього потрібен lodash або structuredClone.

Підсумкова таблиця

МетодПідтримкаКопіює вкладені об'єктиЗберігає типи (Date, Map, Set)Копіює функції
structuredClone()Сучасні браузери, Node.js 17+ТакТакНі
JSON.parse(JSON.stringify())СкрізьТакНіНі
_.cloneDeep()Через бібліотеку lodashТакТакНі
Рекурсивна функціяРучна реалізаціяТакНіНі

В одній фразі:

Щоб зробити надійну глибоку копію, беріть structuredClone(obj), коли потрібна вбудована функція, і _.cloneDeep(obj), коли проєкт великий і важлива стабільність.

Типові помилки

  • Плутати глибоку копію з поверхневою. { ...obj } створює новий об'єкт, але вкладені структури залишаються спільними.
  • Клонувати через JSON об'єкт із датами. Після такого клонування Date стає рядком, і clone.createdAt.getTime() кидає помилку.
  • Очікувати методи після клонування. Ані structuredClone(), ані JSON-трюк, ані проста рекурсія не переносять прототип, тому екземпляр класу стає звичайним об'єктом.
  • Викликати structuredClone() на об'єкті з функцією. Він кидає DataCloneError, а не мовчки пропускає властивість, як JSON.
  • Використовувати наївну рекурсію на циклічних структурах. Без кешу вже скопійованих об'єктів (наприклад, через WeakMap) вона зациклиться і переповнить стек.
  • Тягнути lodash заради одного виклику в сучасному середовищі. Там уже є вбудований structuredClone().

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.