structuredClone() создаёт глубокую копию значения.
Это глобальная функция JavaScript, а не метод Object, но чаще всего её используют именно для копирования объектов, массивов и других сложных структур данных.
Синтаксис
structuredClone(value)
structuredClone(value, options)
value - значение, которое нужно скопировать.
options - дополнительные настройки, например transfer для некоторых типов данных.
Что делает structuredClone()
Функция создаёт новую независимую копию значения.
const user = {
name: "Anna",
address: {
city: "Moscow",
},
};
const copy = structuredClone(user);
copy.address.city = "Paris";
console.log(user.address.city); // "Moscow"
console.log(copy.address.city); // "Paris"
Это главное, что важно запомнить: вложенные объекты здесь тоже копируются, а не остаются общими ссылками.
Что возвращает structuredClone()
structuredClone() возвращает новое значение того же общего типа.
const arr = [1, 2, { a: 3 }];
const copy = structuredClone(arr);
console.log(copy); // [1, 2, { a: 3 }]
console.log(copy === arr); // false
Если копируется объект или массив, результатом будет новая независимая структура.
Это глубокая копия
В отличие от spread и Object.assign(), structuredClone() копирует не только верхний уровень.
const user = {
profile: {
city: "Moscow",
},
};
const copy = structuredClone(user);
copy.profile.city = "Paris";
console.log(user.profile.city); // "Moscow"
Поэтому structuredClone() часто выбирают именно тогда, когда поверхностной копии уже недостаточно.
structuredClone() и spread
Spread делает только поверхностную копию:
const user = {
profile: {
city: "Moscow",
},
};
const copy = { ...user };
copy.profile.city = "Paris";
console.log(user.profile.city); // "Paris"
structuredClone() в такой ситуации создаёт уже независимую вложенную копию.
structuredClone() и Object.assign()
С Object.assign() история такая же: это тоже только поверхностное копирование.
const user = {
profile: {
city: "Moscow",
},
};
const copy = Object.assign({}, user);
copy.profile.city = "Paris";
console.log(user.profile.city); // "Paris"
Если нужна именно глубокая копия, structuredClone() подходит лучше.
Циклические ссылки
Это одна из сильных сторон structuredClone().
Он умеет копировать структуры, где объект ссылается сам на себя.
const user = { name: "Anna" };
user.self = user;
const copy = structuredClone(user);
console.log(copy.self === copy); // true
Это удобно, потому что многие другие способы копирования на таком месте ломаются.
Date, Map, Set, RegExp
structuredClone() умеет работать не только с обычными объектами и массивами.
Он также клонирует многие встроенные типы:
DateMapSetRegExp
Пример:
const date = new Date("2024-01-01T00:00:00Z");
const copy = structuredClone(date);
console.log(copy instanceof Date); // true
console.log(copy.toISOString()); // "2024-01-01T00:00:00.000Z"
Пример с Map:
const map = new Map([["a", 1]]);
const copy = structuredClone(map);
console.log(copy instanceof Map); // true
console.log(copy.get("a")); // 1
Функции не клонируются
Это важное ограничение.
Если внутри значения есть функция, structuredClone() выбросит ошибку DataCloneError.
const user = {
name: "Anna",
greet() {
console.log("Hi");
},
};
structuredClone(user); // DataCloneError
Symbol тоже не клонируется как значение
Если попытаться клонировать Symbol, будет ошибка.
structuredClone(Symbol("id")); // DataCloneError
Поэтому structuredClone() подходит не для всех типов данных.
Пользовательские классы и прототипы
Это важный нюанс, о котором легко забыть.
Если клонировать экземпляр пользовательского класса, его прототип обычно не сохраняется как у исходного объекта.
class User {
constructor(name) {
this.name = name;
}
}
const user = new User("Anna");
const copy = structuredClone(user);
console.log(Object.getPrototypeOf(copy) === User.prototype); // false
console.log(Object.getPrototypeOf(copy) === Object.prototype); // true
То есть по данным копия похожа, но как экземпляр класса она уже не полностью та же самая.
Getter, setter и дескрипторы не сохраняются как есть
structuredClone() копирует данные, а не точную структуру дескрипторов свойства.
Например, getter может превратиться в обычное значение.
const user = {
first: "Anna",
last: "Smith",
get fullName() {
return `${this.first} ${this.last}`;
},
};
const copy = structuredClone(user);
console.log(copy.fullName); // "Anna Smith"
Но это уже не обязательно getter в том же виде, что был у исходного объекта.
Если нужна точная копия дескрипторов, обычно используют связку:
Object.defineProperties({}, Object.getOwnPropertyDescriptors(source))
Неперечисляемые и символьные свойства
На такие свойства не стоит рассчитывать как на надёжно сохранённые “один в один”.
structuredClone() в первую очередь ориентирован на перенос данных, а не на точное копирование всех настроек объекта.
Если важны неперечисляемые свойства, символы или тонкая настройка дескрипторов, лучше смотреть в сторону методов работы с дескрипторами.
structuredClone() и JSON
Раньше для копирования часто писали так:
const copy = JSON.parse(JSON.stringify(value));
Но у такого способа есть ограничения:
- теряются
Date,Map,Set,RegExp - ломаются циклические ссылки
- часть данных может потеряться или превратиться в другое
structuredClone() в этом плане надёжнее и современнее.
Передача ownership через transfer
У structuredClone() есть второй аргумент options, где можно использовать transfer.
Это более специальный сценарий для некоторых типов данных вроде ArrayBuffer.
structuredClone(value, {
transfer: [buffer],
})
Такой вариант нужен реже, но полезно знать, что он существует.
Когда это полезно
structuredClone() удобно использовать, когда:
- нужна глубокая копия объекта или массива
- в данных есть вложенные структуры
- нужно корректно копировать
Date,Map,Set,RegExp - нужно безопасно работать с циклическими ссылками
Пример:
const settings = {
theme: "dark",
user: {
name: "Anna",
},
};
const nextSettings = structuredClone(settings);
nextSettings.user.name = "Ivan";
console.log(settings.user.name); // "Anna"
console.log(nextSettings.user.name); // "Ivan"
Частые ошибки
Думать, что structuredClone() работает как точная копия объекта со всеми дескрипторами
const copy = structuredClone(source);
Метод копирует данные, но не гарантирует сохранение всей низкоуровневой структуры свойства один в один.
Использовать structuredClone() для функций
const value = {
fn() {},
};
structuredClone(value); // DataCloneError
Функции этот метод не клонирует.
Ждать, что экземпляр класса сохранит свой прототип как есть
class User {
constructor(name) {
this.name = name;
}
}
const user = new User("Anna");
const copy = structuredClone(user);
По данным копия будет похожей, но прототип пользовательского класса может не сохраниться.
Быстрая памятка
structuredClone()создаёт глубокую копию значения- функция умеет работать с вложенными структурами и циклическими ссылками
- она поддерживает многие встроенные типы вроде
Date,Map,Set,RegExp - функции и
Symbolкак значение не клонируются - это глобальная функция, а не метод
Object
Коротко
structuredClone() - это современный способ сделать глубокую копию значения в JavaScript. Главное, что стоит запомнить: он хорошо копирует данные и сложные структуры, но не предназначен для точного переноса функций, пользовательских прототипов и всех дескрипторов свойств один в один.