Коротка відповідь
middleDeep clone – це рекурсивне копіювання всіх вкладених об’єктів і масивів. Використовуємо `JSON.parse(JSON.stringify(obj))` для простих випадків, а для більш складних – рекурсивну функцію з Map‑ом для циклів. Приклад: ```js const clone = (obj, map = new WeakMap()) => { if (!obj || typeof obj !== 'object') return obj; if (map.has(obj)) return map.get(obj); const copy = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj)); map.set(obj, copy); for (const key of Reflect.ownKeys(obj)) { copy[key] = clone(obj[key], map); } return copy; }; ```
Повне пояснення
Що це і навіщо
Deep clone створює повну копію об’єкта, включаючи всі вкладені структури. Це потрібне, коли треба змінювати копію без впливу на оригінал (наприклад, у Redux‑store або при тестуванні).
Принцип роботи / алгоритм
Функція перевіряє тип значення: якщо це примітив, повертається самостійно. Якщо об’єкт або масив – створюється новий екземпляр того ж типу, а потім рекурсивно копіюються всі власні ключі. Для уникнення циклічних посилань використовується WeakMap.
Реалізація (JS / TS / CSS)
// Простий варіант – JSON, але не підтримує функції, Date, RegExp
const simpleClone = obj => JSON.parse(JSON.stringify(obj));
// Рекурсивний варіант з WeakMap
function deepClone<T>(obj: T, map = new WeakMap()): T {
if (!obj || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const copy = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
map.set(obj, copy);
for (const key of Reflect.ownKeys(obj)) {
copy[key] = deepClone((obj as any)[key], map);
}
return copy;
}
Тестування
import { test, expect } from 'vitest';
test('deepClone preserves nested objects', () => {
const a = { x: { y: 1 } };
const b = deepClone(a);
expect(b).toEqual(a);
expect(b.x).not.toBe(a.x); // різні інстанси
});
Проблеми та edge cases
- Циклічні посилання – обробляються через
WeakMap. - Date, RegExp, Map, Set – потребують спеціальної обробки (
new Date(obj)тощо). - Функції – копіюються як посилання; якщо потрібна нова функція, треба її переписати.
- Symbol‑ключі – копіюються через
Reflect.ownKeys. - Проблеми з прототипом –
Object.create(Object.getPrototypeOf(obj))зберігає правильний прототип.
Оптимізація та продуктивність
- Для великих об’єктів використовуйте
structuredClone(ES2021) – швидше і підтримує більшості типів. - Якщо потрібна лише поверхня копія, застосуйте
Object.assignабо розпакування.
Без JS / архітектурні альтернативи
У випадку, коли об’єкт лише для передачі даних (наприклад, у React‑props), можна передавати його як JSON.stringify/parse через URL або localStorage.
Cheatsheet + follow-up
- Cheatsheet:
structuredClone(obj)→ deep clone з підтримкою Date, RegExp, Map, Set. - Interview question: Як уникнути проблем з циклічними посиланнями при deep clone?
- Follow‑up: Як реалізувати deep merge замість clone?