Самый эффективный способ глубокого клонирования объекта в JavaScript
Лучший способ: встроенный метод structuredClone()
Начиная с Node.js 17 и современных браузеров (Chrome 98+, Firefox 94+, Safari 15.4+), появился встроенный метод structuredClone(). Это официально рекомендуемый способ глубокого клонирования, который решает большинство проблем старых подходов.
const original = {
name: "Алиса",
age: 30,
address: { city: "Москва", zip: 101000 },
hobbies: ["чтение", "плавание"],
birthDate: new Date("1994-05-20"),
nested: { deep: { value: 42 } }
};
const clone = structuredClone(original);
// Проверяем независимость
clone.address.city = "Санкт-Петербург";
clone.hobbies.push("теннис");
clone.birthDate.setFullYear(2000);
console.log(original.address.city); // "Москва" — не изменилось
console.log(original.hobbies); // ["чтение", "плавание"] — не изменилось
console.log(original.birthDate); // 1994-05-20 — не изменилось
Что structuredClone() умеет?
- Глубокое клонирование всех вложенных объектов и массивов
- Поддержка Date, Map, Set, RegExp, ArrayBuffer и других типов
- Обработка циклических ссылок — не вызывает бесконечную рекурсию
- Высокая производительность — реализован на уровне движка JavaScript
Что structuredClone() НЕ клонирует?
- Функции — вызовет ошибку "DataCloneError"
- Symbol — игнорируются
- WeakMap, WeakSet — не поддерживаются
- Прототипы и геттеры/сеттеры — копируются только значения
Когда structuredClone() недоступен: альтернативы
Если нужно поддерживать старые браузеры или Node.js < 17, используйте проверенные библиотеки:
// Вариант 1: Lodash (самый популярный)
import _ from 'lodash';
const lodashClone = _.cloneDeep(original);
// Вариант 2: Простая рекурсивная функция (для учебных целей)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Array) return obj.map(item => deepClone(item));
const clonedObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
Почему JSON.parse/JSON.stringify — плохой выбор?
const obj = {
name: "Тест",
date: new Date(),
func: () => console.log("привет"),
undef: undefined,
nan: NaN
};
const badClone = JSON.parse(JSON.stringify(obj));
console.log(badClone.date); // Строка, а не Date!
console.log(badClone.func); // undefined — функция потеряна
console.log(badClone.undef); // undefined — ключ пропал
console.log(badClone.nan); // null — NaN превратился в null
Рекомендация
В 2024 году для 95% случаев используйте structuredClone(). Если нужна поддержка функций — перепроектируйте архитектуру (функции не должны быть частью данных). Для легаси-проектов установите lodash через npm. И никогда не используйте JSON.parse/stringify для production-кода — это источник трудноуловимых багов.
Альтернативный подход: библиотека Lodash с глубоким клонированием
Если вы работаете в проекте, где уже используется Lodash (одна из самых популярных утилитарных библиотек), или вам нужно клонировать объекты с функциями и символами — _.cloneDeep() будет лучшим выбором. В отличие от structuredClone(), Lodash обрабатывает функции (копирует ссылки на них) и символы.
import _ from 'lodash';
const complexObj = {
method: function() { return 42; },
[Symbol('id')]: 123,
nested: { data: [1, 2, { x: 10 }] }
};
const cloned = _.cloneDeep(complexObj);
console.log(cloned.method()); // 42 — функция работает
console.log(cloned.nested.data[2].x); // 10 — глубоко скопировано
Когда использовать Lodash вместо structuredClone?
- Нужна поддержка старых браузеров (IE11, Safari < 15.4)
- Объекты содержат функции или символы
- Вы уже используете Lodash в проекте (не стоит добавлять библиотеку только ради cloneDeep)
Но помните: _.cloneDeep() медленнее нативного structuredClone() примерно в 2-4 раза для больших объектов. Для большинства веб-приложений эта разница незаметна, но если вы клонируете гигабайтные структуры данных — предпочтите нативный метод.