Этот вопрос проверяет понимание структур данных в JavaScript и умение реализовывать алгоритмы для их обхода и обработки.
Глубокое копирование можно реализовать с помощью рекурсивной функции, которая вручную создает новый объект или массив и копирует все его свойства. Для каждого свойства, если оно является объектом, функция вызывает сама себя. Основные сложности: обработка циклических ссылок (когда объект ссылается сам на себя), копирование специальных объектов (как Date, Map, Set) и поддержка различных типов данных.
Реализация глубокого копирования "вручную" требует тщательного обхода всех свойств исходного объекта и правильного копирования каждого значения.
Базовая реализация рекурсивной функции:
function deepClone(value, visited = new WeakMap()) {
// 1. Обработка примитивов и null/undefined
if (value === null || typeof value !== 'object') {
return value;
}
// 2. Обработка циклических ссылок
if (visited.has(value)) {
return visited.get(value);
}
// 3. Обработка специальных встроенных объектов
if (value instanceof Date) {
return new Date(value);
}
if (value instanceof Map) {
const copy = new Map();
visited.set(value, copy);
value.forEach((val, key) => {
copy.set(key, deepClone(val, visited));
});
return copy;
}
// Аналогично для Set, RegExp и других...
// 4. Обработка массивов и обычных объектов
const copy = Array.isArray(value) ? [] : {};
visited.set(value, copy); // Сохраняем копию для проверки циклических ссылок
// Рекурсивное копирование всех свойств
for (let key in value) {
if (value.hasOwnProperty(key)) {
copy[key] = deepClone(value[key], visited);
}
}
return copy;
}Сложности, которые могут возникнуть:
Циклические ссылки: Если объект A ссылается на объект B, а B ссылается на A, наивная рекурсивная функция войдет в бесконечный цикл и вызовет переполнение стека. Для решения этой проблемы используется структура данных WeakMap для запоминания уже скопированных объектов.
"Хрупкие" объекты: Некоторые объекты, like Date, Map, Set, RegExp, при простом копировании свойств потеряют свою внутреннюю структуру и функциональность. Их нужно создавать заново с помощью их конструкторов.
Производительность: Рекурсивный обход больших и сложных объектов может быть медленным.
Неперечислимые свойства и геттеры/сеттеры: Базовая реализация с for...in копирует только enumerable-свойства. Она не копирует дескрипторы свойств (getters/setters) или свойства из цепочки прототипов.
Вывод:
Ручная реализация полезна для понимания механизмов языка, но в продакшене лучше использовать встроенные методы (StructuredClone) или проверенные библиотеки (например, lodash.cloneDeep), которые уже решают все эти сложности.