Подробный обзор функции structuredClone: назначение, поддерживаемые типы, ограничения и отличия от JSON-клонирования и других подходов.
Функция structuredClone
Копирование объектов — задача, которая кажется тривиальной, пока в структуре не появятся вложенные ссылки, циклические зависимости или типы данных вродеMap,DateиArrayBuffer. ФункцияstructuredCloneрешает именно эту задачу на уровне платформы.
Долгое время в JavaScript не было встроенного способа сделать глубокую копию (deep clone) произвольного значения. Разработчики либо писали рекурсивные обходы вручную, либо использовали трюк JSON.parse(JSON.stringify(value)), либо подключали сторонние библиотеки вроде Lodash. У каждого из этих решений были свои ограничения: JSON-подход терял функции, undefined, Date, Map, Set и не умел работать с циклическими ссылками; ручные обходы легко ломались на экзотических типах.
Функция structuredClone появилась как стандартный ответ на эту проблему. Она выполняет структурное клонирование — алгоритм, который уже много лет использовался браузерами при передаче данных через postMessage, при работе с IndexedDB и в других API. Теперь тот же механизм доступен напрямую как глобальная функция.
Что такое структурное клонирование
Структурное клонирование (structured clone) — это алгоритм, который создаёт глубокую копию значения, проходя по его внутренней структуре. В отличие от поверхностного копирования через Object.assign или spread-оператор, он копирует не только верхний уровень, но и все вложенные объекты, массивы и другие контейнеры. При этом сохраняются ссылочные связи: если один и тот же объект встречается в структуре дважды, в копии он тоже будет представлен одним объектом, а не двумя разными.
Алгоритм корректно обрабатывает циклические ссылки. Например, объект, который ссылается сам на себя, будет скопирован без бесконечной рекурсии.
const original = { name: "узел" };
original.self = original;
const copy = structuredClone(original);
console.log(copy.self === copy); // true
console.log(copy === original); // false
Поддерживаемые типы данных
Структурное клонирование умеет работать с большинством встроенных типов, которые встречаются в обычном коде. Важно понимать, что именно копируется по значению, а что не поддерживается вовсе.
Поддерживаются:
- примитивы:
string,number,boolean,null,undefined,bigint; Date,RegExp;Map,Set,WeakMap(только как значение, не как ключ),WeakSet;ArrayBuffer,TypedArray,DataView;Blob,File,FileList,ImageData;- простые объекты и массивы;
Errorи его подтипы.
Не поддерживаются и приводят к исключению DataCloneError:
- функции;
- DOM-узлы;
- свойства-аксессоры (геттеры и сеттеры) — копируется только значение;
- прототипы объектов — копия получает
Object.prototypeилиArray.prototype, даже если оригинал был экземпляром пользовательского класса; - символы в качестве значений свойств;
WeakRef,FinalizationRegistry.
Структурное клонирование — это копирование данных, а не поведения. Всё, что связано с логикой (функции, методы, геттеры), в копию не переносится.
Отличия от JSON-клонирования
Самый распространённый «народный» способ глубокого копирования — прогнать значение через JSON. Этот приём работает, но молча теряет данные, что делает его опасным в нетривиальных структурах.
const source = {
date: new Date(),
map: new Map([["k", 1]]),
undef: undefined,
fn: () => {}
};
const jsonCopy = JSON.parse(JSON.stringify(source));
// date — строка, а не Date
// map — пустой объект {}
// undef — свойство исчезло
// fn — свойство исчезло
const structuredCopy = structuredClone(source);
// date — Date
// map — Map
// undef — undefined
// fn — выбросит DataCloneError
Разница принципиальная: JSON-подход преобразует данные в текстовый формат и обратно, теряя всё, что не выражается в JSON. Структурное клонирование работает на уровне внутренних представлений объектов и сохраняет типы. Однако за это приходится платить: наличие функции в структуре приводит к ошибке, а не к тихому пропуску.
Синтаксис и возвращаемое значение
Функция принимает одно обязательное значение и возвращает его глубокую копию.
const clone = structuredClone(value);
// с опцией передачи ресурсов (например, ArrayBuffer):
const clone2 = structuredClone(value, { transfer: [buffer] });
Опция transfer позволяет передать владение ArrayBuffer из оригинала в копию, а не копировать его. После передачи исходный буфер становится недоступным (его byteLength равен нулю). Это полезно для больших бинарных данных, когда копирование избыточно.
Функция синхронная, не принимает колбэков и не возвращает промис. При ошибке выбрасывает исключение DataCloneError (наследник DOMException).
Ограничения и типичные ошибки
Главное ограничение — невозможность клонировать функции, DOM-узлы и объекты с кастомными прототипами. Второе, менее очевидное: копия не сохраняет связь с классом оригинала.
class User {
constructor(name) { this.name = name; }
greet() { return "Привет, " + this.name; }
}
const u = new User("Анна");
const c = structuredClone(u);
console.log(c instanceof User); // false
console.log(c.greet); // undefined
Если требуется сохранить поведение, структурное клонирование не подходит — нужно либо реализовать собственный метод копирования, либо использовать паттерн «клонирование через конструктор». Также стоит помнить, что свойства-аксессоры в копии превращаются в обычные свойства со значением, полученным в момент клонирования.
Когда использовать structuredClone
Функция уместна в ситуациях, когда нужно получить независимую копию данных, состоящих из стандартных типов: конфигураций, снимков состояния, сообщений, результатов запросов. Она хорошо подходит для передачи данных между воркерами, для сохранения снимков перед мутацией и для отката изменений.
Не стоит применять её к объектам с методами, к DOM-структурам и к значениям, где важна идентичность класса. В этих случаях разумнее явно описать логику копирования.
Что запомнить
structuredClone— встроенная глобальная функция для глубокого копирования значений.- Она использует алгоритм структурного клонирования, тот же, что применяется в
postMessageиIndexedDB. - Корректно обрабатывает циклические ссылки и сохраняет разделяемые ссылки внутри структуры.
- Поддерживает
Date,Map,Set,ArrayBuffer,TypedArray,Errorи другие стандартные типы. - Не клонирует функции, DOM-узлы, символы и объекты с пользовательскими прототипами — выбрасывает
DataCloneError. - Не сохраняет прототип: копия экземпляра класса становится простым объектом.
- Опция
transferпозволяет передать владениеArrayBufferвместо копирования. - Предпочтительнее
JSON.parse(JSON.stringify(...)), так как не теряет типы и не игнорирует ошибки. - Не заменяет пользовательскую логику копирования там, где важно поведение объекта.