TGViewer
Настоящий JavaScript Настоящий JavaScript @true_js · 5.89K subscribers
Post #3580 348
⁣Глубокое клонирование с сохранением типов: structuredClone vs. JSON vs. кастомная рекурсия для Map, Set, WeakMap и циклических ссылок

Глубокое клонирование — один из тех сценариев, где стандартные решения либо теряют типы, либо падают с ошибками. В production это критично: при клонировании состояния в стейт-менеджменте, кэшировании данных из API или передаче объектов между воркерами. Частая ошибка — полагаться на JSON.parse(JSON.stringify(obj)) во всех случаях, не учитывая, что он не поддерживает Map, Set, Date, BigInt и циклические ссылки.

✅ structuredClone — стандарт для большинства сценариев
Современный API, доступный в браузерах и Node.js 17+. Поддерживает Map, Set, ArrayBuffer, Blob, Date, RegExp, Error и циклические ссылки. Работает быстро, но не клонирует функции, WeakMap и WeakSet, а также теряет прототипы с геттерами и сеттерами. В production — базовый выбор.

const obj = { 
a: 1,
map: new Map([['key', 'val']]),
date: new Date()
};
obj.self = obj;
const clone = structuredClone(obj);
// clone.map.get('key') -> 'val'
// clone.self === clone -> true


⚠️ JSON.parse/JSON.stringify — опасный компромисс
Подходит только для плоских объектов с примитивами. Потеря undefined, функций, BigInt, всех типов коллекций и падение на циклических ссылках делает его ненадёжным даже для простых API-ответов. Используйте только для чистых JSON-данных, где типобезопасность не требуется.

🔧 Кастомная рекурсия для полного контроля
Когда нужно сохранить прототипы, геттеры или работать с WeakMap/WeakSet, пишите рекурсию с WeakMap для защиты от циклов. Пример кукбука для экзотических типов:

function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);

let clone;
if (obj instanceof WeakMap) {
clone = new WeakMap();
hash.set(obj, clone);
obj.forEach((val, key) => clone.set(key, deepClone(val, hash)));
return clone;
}

if (obj instanceof Map) {
clone = new Map();
hash.set(obj, clone);
obj.forEach((val, key) => clone.set(key, deepClone(val, hash)));
return clone;
}
// Аналогично для Set, WeakSet

clone = Object.create(Object.getPrototypeOf(obj));
hash.set(obj, clone);
const descriptors = Object.getOwnPropertyDescriptors(obj);
for (const key of Object.keys(descriptors)) {
const desc = descriptors[key];
if (desc.get || desc.set) {
Object.defineProperty(clone, key, desc);
} else {
clone[key] = deepClone(desc.value, hash);
}
}
return clone;
}


Практический совет: для WeakMap аккуратно обрабатывайте ключи — они объекты, и их тоже нужно клонировать, иначе уникальность ссылок потеряется. Типичная ошибка — забыть обработать WeakMap в решении, что приводит к утечкам при кэшировании.

Вывод: structuredClone покрывает 90% сценариев, а кастомную рекурсию оставляйте для случаев, где важны прототипы, функции и WeakMap/WeakSet — тогда баланс между производительностью и типобезопасностью оправдан.
More from @true_js
  1. Oct 3, 2026Post #3963
  2. Oct 2, 2026😅 Айтишник отправил в одну компанию три одинаковых резюме и только одно дошло до финала О…
  3. Oct 2, 2026🤣 Правильно расставленные приоритеты в моей жизни би лайк: 💥 xCode Journal
  4. Oct 1, 2026🤯 Люди взбунтовались против «пыточной для ИИ» На GitHub заметили открытый проект AI Tortu…
  5. Sep 30, 2026Книга из переписки без сервера: canvas, jsPDF и брошюра для печати прямо в браузере Пет-пр…
  6. Sep 30, 2026Экономика клиентской разработки с AI С AI в клиентской разработке появился смысл делать то…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →