← Назад к вопросам

Глубокое клонирование объекта

2.0 Middle🔥 131 комментариев
#JavaScript Core#Архитектура и паттерны

Условие

Напишите функцию deepClone(obj), которая создаёт глубокую копию объекта, включая все вложенные объекты и массивы.

Требования

  1. Функция должна корректно обрабатывать:

    • Примитивные значения
    • Вложенные объекты
    • Массивы (в том числе вложенные)
    • null и undefined
    • Date объекты
  2. Изменения в клоне не должны влиять на оригинал

Пример

const original = {
  name: "John",
  address: {
    city: "Moscow",
    coords: [55.75, 37.62]
  },
  date: new Date("2024-01-01")
};

const clone = deepClone(original);
clone.address.city = "SPb";

console.log(original.address.city); // "Moscow"
console.log(clone.address.city);    // "SPb"

Бонус

Обработайте циклические ссылки в объекте.

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение: Глубокое клонирование объекта

Почему глубокое клонирование важно?

При работе со сложными структурами данных в JavaScript обычное присваивание создаёт ссылку на объект, а не его копию. Изменение значений во вложенных объектах или массивах повлияет на оригинал. Глубокое клонирование создаёт полностью независимую копию, включая все вложенные структуры.

Базовая рекурсивная реализация

function deepClone(obj) {
  // Обработка примитивов
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  
  // Обработка Date
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  
  // Обработка Array
  if (Array.isArray(obj)) {
    return obj.map(item => deepClone(item));
  }
  
  // Обработка Object
  const cloned = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key]);
    }
  }
  
  return cloned;
}

Версия с обработкой циклических ссылок

function deepClone(obj, seen = new WeakMap()) {
  // Обработка примитивов
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  
  // Проверка на циклические ссылки
  if (seen.has(obj)) {
    return seen.get(obj);
  }
  
  // Обработка Date
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  
  // Обработка Array
  if (Array.isArray(obj)) {
    const cloned = [];
    seen.set(obj, cloned);
    obj.forEach((item, index) => {
      cloned[index] = deepClone(item, seen);
    });
    return cloned;
  }
  
  // Обработка Object
  const cloned = {};
  seen.set(obj, cloned);
  
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key], seen);
    }
  }
  
  return cloned;
}

TypeScript версия с расширенной типизацией

function deepClone<T>(obj: T, seen: WeakMap<object, any> = new WeakMap()): T {
  // Примитивы
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  
  // Циклические ссылки
  if (seen.has(obj as object)) {
    return seen.get(obj as object);
  }
  
  // Date
  if (obj instanceof Date) {
    return new Date((obj as Date).getTime()) as unknown as T;
  }
  
  // Array
  if (Array.isArray(obj)) {
    const cloned: any[] = [];
    seen.set(obj, cloned);
    obj.forEach((item, index) => {
      cloned[index] = deepClone(item, seen);
    });
    return cloned as unknown as T;
  }
  
  // Object
  const cloned = {} as T;
  seen.set(obj as object, cloned);
  
  (Object.keys(obj) as Array<keyof T>).forEach(key => {
    cloned[key] = deepClone((obj as any)[key], seen);
  });
  
  return cloned;
}

Примеры использования

// Базовый пример
const user = {
  name: "Alice",
  profile: { age: 30, city: "Moscow" }
};

const userCopy = deepClone(user);
userCopy.profile.age = 25;

console.log(user.profile.age); // 30
console.log(userCopy.profile.age); // 25

// С датами
const event = {
  name: "Conference",
  date: new Date("2024-12-01")
};

const eventClone = deepClone(event);
eventClone.date.setMonth(5);

console.log(event.date.getMonth()); // 11
console.log(eventClone.date.getMonth()); // 5

// С циклическими ссылками
const parent = { name: "Parent" };
const child = { name: "Child", parent };
parent.child = child;

const clonedParent = deepClone(parent);
console.log(clonedParent.child.parent === clonedParent); // true

Альтернативные подходы

JSON метод (ограничения):

const clone = JSON.parse(JSON.stringify(obj));
// Проблемы: теряет Date, undefined, функции, Symbol

structuredClone (современный API):

const clone = structuredClone(obj);
// Плюсы: работает в современных браузерах, обрабатывает циклы
// Минусы: не поддерживается в старых браузерах

Ключевые моменты

  • WeakMap для циклических ссылок: слабые ссылки позволяют сборщику мусора очищать объекты
  • hasOwnProperty: проверяет только собственные свойства, исключая наследованные
  • instanceof Date: Date нужно обрабатывать отдельно, так как это объект
  • Рекурсия: каждый уровень вложенности обрабатывается функцией рекурсивно
  • Производительность: WeakMap хранит ссылки без утечек памяти
Глубокое клонирование объекта | PrepBro