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

Deep clone объекта

1.7 Middle🔥 171 комментариев
#Node.js и JavaScript#Алгоритмы и структуры данных

Условие

Напишите функцию deepClone(obj), которая создает глубокую копию объекта:

function deepClone(obj) {
  // Ваш код
}

const original = {
  name: "John",
  age: 30,
  address: {
    city: "Moscow",
    zip: 123456
  },
  hobbies: ["reading", "coding"]
};

const cloned = deepClone(original);
cloned.address.city = "London";
cloned.hobbies.push("gaming");

console.log(original.address.city); // "Moscow" (не изменился)
console.log(original.hobbies); // ["reading", "coding"] (не изменился)

Что проверяется

  • Рекурсия
  • Работа с объектами и массивами
  • Обработка разных типов данных (Date, RegExp и т.д.)

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

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

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

Решение: Deep Clone объекта

Базовая реализация

function deepClone<T>(obj: T): T {
  // Примитивные типы и null
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // Date
  if (obj instanceof Date) {
    return new Date(obj.getTime()) as unknown as T;
  }
  
  // RegExp
  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags) as unknown as T;
  }
  
  // Массивы
  if (Array.isArray(obj)) {
    const clonedArray: any[] = [];
    for (let i = 0; i < obj.length; i++) {
      clonedArray[i] = deepClone(obj[i]);
    }
    return clonedArray as T;
  }
  
  // Объекты
  if (obj instanceof Object) {
    const clonedObject: any = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        clonedObject[key] = deepClone((obj as any)[key]);
      }
    }
    return clonedObject as T;
  }
  
  return obj;
}

С обработкой циклических ссылок (WeakMap)

function deepClone<T>(obj: T, visited = new WeakMap<any, any>()): T {
  // Примитивные типы
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // Циклические ссылки
  if (visited.has(obj)) {
    return visited.get(obj);
  }
  
  let cloned: any;
  
  // Date
  if (obj instanceof Date) {
    cloned = new Date(obj.getTime());
    visited.set(obj, cloned);
    return cloned as T;
  }
  
  // RegExp
  if (obj instanceof RegExp) {
    cloned = new RegExp(obj.source, obj.flags);
    visited.set(obj, cloned);
    return cloned as T;
  }
  
  // Map
  if (obj instanceof Map) {
    cloned = new Map();
    visited.set(obj, cloned);
    obj.forEach((value, key) => {
      cloned.set(deepClone(key, visited), deepClone(value, visited));
    });
    return cloned as T;
  }
  
  // Set
  if (obj instanceof Set) {
    cloned = new Set();
    visited.set(obj, cloned);
    obj.forEach((value) => {
      cloned.add(deepClone(value, visited));
    });
    return cloned as T;
  }
  
  // Массивы
  if (Array.isArray(obj)) {
    cloned = [];
    visited.set(obj, cloned);
    for (let i = 0; i < obj.length; i++) {
      cloned[i] = deepClone(obj[i], visited);
    }
    return cloned as T;
  }
  
  // Объекты
  if (obj instanceof Object) {
    cloned = Object.create(Object.getPrototypeOf(obj));
    visited.set(obj, cloned);
    
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        cloned[key] = deepClone((obj as any)[key], visited);
      }
    }
    
    return cloned as T;
  }
  
  return obj;
}

Примеры

// Пример 1: Простой объект
const original = {
  name: "John",
  age: 30,
  address: {
    city: "Moscow",
    zip: 123456
  },
  hobbies: ["reading", "coding"]
};

const cloned = deepClone(original);
cloned.address.city = "London";
cloned.hobbies.push("gaming");

console.log(original.address.city); // "Moscow"
console.log(original.hobbies); // ["reading", "coding"]

// Пример 2: С Date и RegExp
const withDate = {
  created: new Date(),
  pattern: /test/gi
};
const clonedDate = deepClone(withDate);
console.log(clonedDate.created instanceof Date); // true

// Пример 3: С циклическими ссылками
const circular = { name: 'node' };
circular.self = circular; // циклическая ссылка
const clonedCircular = deepClone(circular);
console.log(clonedCircular.self === clonedCircular); // true

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

1. Рекурсия

  • Обрабатываем каждый уровень вложенности
  • Базовый случай: примитивные типы

2. instanceof проверки

  • Date, RegExp, Array имеют свои конструкторы
  • Массивы — это тип Object, поэтому Array.isArray() важен

3. WeakMap для циклических ссылок

  • Хранит соответствие оригинальных объектов и их копий
  • Автоматически удаляется из памяти
  • Предотвращает бесконечную рекурсию

4. hasOwnProperty

  • Пропускаем унаследованные свойства
  • Копируем только собственные свойства

Vs structuredClone (Modern)

// Встроенный метод (Node.js 17+)
const cloned = structuredClone(original);
// Плюсы: встроенный, быстрый
// Минусы: не клонирует функции, классы с методами

Тестирование

test('deepClone простого объекта', () => {
  const obj = { a: 1, b: { c: 2 } };
  const cloned = deepClone(obj);
  cloned.b.c = 999;
  expect(obj.b.c).toBe(2);
});

test('deepClone массива', () => {
  const arr = [1, [2, 3], 4];
  const cloned = deepClone(arr);
  cloned[1][0] = 999;
  expect(arr[1][0]).toBe(2);
});

test('deepClone Date', () => {
  const date = new Date();
  const cloned = deepClone({ date });
  expect(cloned.date instanceof Date).toBe(true);
  expect(cloned.date).not.toBe(date);
});

test('deepClone циклических ссылок', () => {
  const obj = { a: 1 };
  obj.self = obj;
  const cloned = deepClone(obj);
  expect(cloned.self === cloned).toBe(true);
});
Deep clone объекта | PrepBro