Глибока копія об'єкта
Щоб зробити глибоку копію об'єкта, використовують вбудований structuredClone(obj), а для складних проєктів _.cloneDeep(obj) з lodash. Глибока копія, це повністю незалежний об'єкт, включно з усіма вкладеними об'єктами й масивами, тому зміни в копії не впливають на оригінал.
Теорія
TL;DR
- Глибока копія відтворює все дерево об'єкта, а не лише верхній рівень.
structuredClone(obj), вбудований, сучасний і швидкий, але не копіює функції та прототипи.JSON.parse(JSON.stringify(obj))працює скрізь, але мовчки втрачає багато типів._.cloneDeep(obj)з lodash, найнадійніший варіант для складних структур.- Власна рекурсивна функція корисна для розуміння механіки, але не покриває
Map,Set,Date,RegExp. - Практичне правило:
structuredClone()за замовчуванням,cloneDeep(), коли потрібна максимальна сумісність.
Швидкий приклад
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 та новіших.
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())
Класичний спосіб, який працює скрізь, навіть у старих браузерах:
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, це найнадійніший варіант.
import _ from 'lodash';
const clone = _.cloneDeep(user);Переваги:
- глибоко копіює будь-які типи даних (включно з
Date,Map,Set,Bufferтощо); - працює стабільно в усіх середовищах.
Недолік:
- потребує встановлення бібліотеки:
npm install lodashСпосіб 4: ручна рекурсивна копія (для розуміння)
Якщо хочете зрозуміти механіку, ось базова реалізація:
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().
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.