Структурированное клонирование: structuredClone
structuredClone— встроенная функция (ES2022 / Node 17+), создающая глубокую копию объекта с поддержкой большинства встроенных типов (Date,Map,Set,ArrayBuffer,RegExp) в отличие отJSON.parse(JSON.stringify(...)).
Зачем нужно
Глубокое копирование объектов — частая задача: нужно изменить вложенные данные не затрагивая оригинал (Redux, иммутабельные обновления). JSON.parse/stringify не копирует Date, Map, Set, undefined, функции, циклические ссылки. structuredClone решает все эти проблемы нативно.
Где используется
- Иммутабельные обновления состояния (Redux, Zustand)
- Передача данных между Web Workers
- Глубокое копирование конфигурационных объектов
- Клонирование данных API-ответов перед модификацией
Базовое использование
const original = {
name: 'Иван',
address: {
city: 'Москва',
coords: [55.75, 37.61]
},
tags: ['admin', 'user']
};
const clone = structuredClone(original);
clone.address.city = 'СПб';
clone.tags.push('moderator');
console.log(original.address.city); // 'Москва' — не изменился
console.log(original.tags); // ['admin', 'user'] — не изменился
Поддерживаемые типы
// Date
const withDate = { created: new Date('2024-01-01') };
const clonedDate = structuredClone(withDate);
console.log(clonedDate.created instanceof Date); // true — правильный тип!
// JSON.parse/stringify:
const jsonClone = JSON.parse(JSON.stringify(withDate));
console.log(typeof jsonClone.created); // 'string' — дата превратилась в строку!
// Map и Set
const withMap = { lookup: new Map([['a', 1], ['b', 2]]) };
const clonedMap = structuredClone(withMap);
console.log(clonedMap.lookup instanceof Map); // true
const withSet = { unique: new Set([1, 2, 3]) };
const clonedSet = structuredClone(withSet);
console.log(clonedSet.unique instanceof Set); // true
// RegExp
const withRegex = { pattern: /hello/gi };
const clonedRegex = structuredClone(withRegex);
console.log(clonedRegex.pattern instanceof RegExp); // true
// ArrayBuffer и TypedArray
const buffer = new ArrayBuffer(16);
const view = new Int32Array(buffer);
view[0] = 42;
const clonedBuffer = structuredClone(buffer);
// clonedBuffer — независимая копия
Циклические ссылки
const obj = { name: 'Тест' };
obj.self = obj; // циклическая ссылка
// JSON.parse/stringify:
// JSON.stringify(obj); // TypeError: Converting circular structure to JSON
// structuredClone обрабатывает корректно!
const clone = structuredClone(obj);
console.log(clone.self === clone); // true — связь сохранена в копии
Передача между Workers (Transferable)
// Второй аргумент: { transfer: [...] } — перемещение (не копирование) буферов
const buffer = new ArrayBuffer(1024);
// Передача с перемещением — buffer становится пустым после этого
const clone = structuredClone(buffer, { transfer: [buffer] });
console.log(buffer.byteLength); // 0 — буфер перемещён, не скопирован
Что НЕ поддерживается
// Функции
const withFn = { fn: => 'hello' };
structuredClone(withFn); // DataCloneError: fn could not be cloned
// DOM-узлы
structuredClone(document.body); // DataCloneError
// WeakMap, WeakSet
structuredClone(new WeakMap); // DataCloneError
// Прототипы не сохраняются для пользовательских классов
class Point { constructor(x, y) { this.x = x; this.y = y; } }
const p = new Point(1, 2);
const cloned = structuredClone(p);
console.log(cloned instanceof Point); // false! — стал обычным объектом
Сравнение методов глубокого клонирования
| Метод | Date | Map/Set | Функции | Циклические | Классы |
|---|---|---|---|---|---|
JSON.parse/stringify |
Нет | Нет | Нет | Нет | Нет |
structuredClone |
Да | Да | Нет | Да | Частично |
Lodash _.cloneDeep |
Да | Да | Нет | Да | Да |
Частые ошибки
1. Клонирование классовых экземпляров
class User {
constructor(name) { this.name = name; }
greet { return `Привет, ${this.name}`; }
}
const u = new User('Анна');
const clone = structuredClone(u);
clone.greet; // TypeError: clone.greet is not a function — метод потерян!
// Решение: ручное клонирование через конструктор
const cloned = new User(u.name);
2. Ошибка с функциями в объектах (молча теряются)
В некоторых средах функции просто игнорируются, в других — выбрасывается DataCloneError. Проверяй структуру перед клонированием.