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

Как могут помочь циклы при клонировании объекта?

1.0 Junior🔥 71 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Циклы при клонировании объектов

Циклы (for, forEach, Object.keys) используются при клонировании объектов для создания глубокой копии, которая полностью независима от исходного объекта.

Проблема поверхностного копирования

Сначала поймём, почему нужно клонирование:

// ПРОБЛЕМА: Поверхностное копирование
const original = {
  name: 'Иван',
  address: { city: 'Москва', zip: '101000' }
};

// Способ 1: Присвоение (не копирует!)
const copy1 = original;
copy1.address.city = 'Санкт-Петербург';
console.log(original.address.city); // 'Санкт-Петербург' - ИЗМЕНИЛСЯ ОРИГИНАЛ!

// Способ 2: Spread оператор (поверхностная копия)
const copy2 = { ...original };
copy2.address.city = 'Казань';
console.log(original.address.city); // 'Казань' - СНОВА ИЗМЕНИЛСЯ!

// Почему? Потому что address всё ещё указывает на тот же объект

Решение 1: Циклы для поверхностного копирования

// Копирование используя for...in цикл
const original = { a: 1, b: 2, c: 3 };

const copy = {};
for (let key in original) {
  if (original.hasOwnProperty(key)) { // Проверяем свои свойства
    copy[key] = original[key];
  }
}

copy.a = 99;
console.log(original.a); // 1 - оригинал не изменился

Решение 2: Глубокое копирование через циклы

Для вложенных объектов нужна рекурсия:

// Глубокое копирование с использованием циклов и рекурсии
function deepClone(obj) {
  // Базовые типы
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // Дата
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  
  // Массив
  if (Array.isArray(obj)) {
    const clone = [];
    for (let i = 0; i < obj.length; i++) {
      clone[i] = deepClone(obj[i]); // Рекурсивный клон
    }
    return clone;
  }
  
  // Объект
  const clone = {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key]); // Рекурсивный клон
    }
  }
  return clone;
}

const original = {
  name: 'Иван',
  age: 25,
  address: {
    city: 'Москва',
    coords: { lat: 55.75, lon: 37.62 }
  },
  hobbies: ['читать', 'кодить']
};

const cloned = deepClone(original);
cloned.address.city = 'Казань';
cloned.hobbies[0] = 'рисовать';

console.log(original.address.city); // 'Москва'
console.log(original.hobbies[0]); // 'читать'

Решение 3: Object.keys с циклом (современный подход)

// Поверхностное копирование
const original = { a: 1, b: 2, c: 3 };
const copy = {};

Object.keys(original).forEach(key => {
  copy[key] = original[key];
});

// Глубокое копирование
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  
  const clone = Array.isArray(obj) ? [] : {};
  
  Object.keys(obj).forEach(key => {
    clone[key] = deepClone(obj[key]); // Рекурсия
  });
  
  return clone;
}

Решение 4: Object.entries (ES2017)

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  
  const clone = Array.isArray(obj) ? [] : {};
  
  Object.entries(obj).forEach(([key, value]) => {
    clone[key] = deepClone(value);
  });
  
  return clone;
}

Практический пример: Клонирование с фильтром

// Клонируем объект, исключая приватные свойства (начинающиеся с _)
function clonePublicProperties(obj) {
  const clone = {};
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key) && !key.startsWith('_')) {
      clone[key] = obj[key];
    }
  }
  
  return clone;
}

const user = {
  name: 'Иван',
  email: 'ivan@example.com',
  _password: '12345' // Приватное свойство
};

const publicUser = clonePublicProperties(user);
console.log(publicUser); // { name: 'Иван', email: 'ivan@example.com' }

Решение 5: Структурированный клон (современный стандарт)

// Встроенный способ для глубокого клонирования
const original = {
  name: 'Иван',
  address: { city: 'Москва' },
  hobbies: ['читать', 'кодить']
};

// structuredClone уже содержит логику циклов внутри
const cloned = structuredClone(original);
cloned.address.city = 'Казань';
console.log(original.address.city); // 'Москва'

// Это эквивалентно нашему deepClone, но встроено в JavaScript

Решение 6: JSON способ (с ограничениями)

// JSON.stringify + JSON.parse также использует внутренние циклы
function jsonClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

const original = {
  name: 'Иван',
  age: 25,
  address: { city: 'Москва' }
};

const cloned = jsonClone(original);
cloned.address.city = 'Казань';
console.log(original.address.city); // 'Москва'

// МИНУСЫ JSON способа:
// - Функции удаляются
// - Даты становятся строками
// - undefined становится null
// - Map, Set теряют типы

Практический пример в React

// Клонирование состояния при обновлении вложенного объекта
function UserProfile() {
  const [user, setUser] = React.useState({
    name: 'Иван',
    address: { city: 'Москва', zip: '101000' }
  });
  
  function updateCity(newCity) {
    // НЕПРАВИЛЬНО - меняет оригинальный объект
    // user.address.city = newCity;
    // setUser(user);
    
    // ПРАВИЛЬНО - клонируем перед изменением
    const updated = JSON.parse(JSON.stringify(user)); // Или structuredClone
    updated.address.city = newCity;
    setUser(updated); // React видит новый объект
  }
  
  return (
    <div>
      <p>{user.address.city}</p>
      <button onclick={() => updateCity('Казань')}>Изменить город</button>
    </div>
  );
}

Сравнение методов клонирования

МетодГлубинаПроизводительностьСложностьОграничения
for...in1 уровеньБыстроНизкаяТолько поверхность
Рекурсия + for...inГлубокаяСреднеСредняяМожет быть медленно
Object.keys1 уровеньБыстроНизкаяТолько поверхность
JSON.parse/stringifyГлубокаяМедленноНизкаяТеряет функции, даты
structuredCloneГлубокаяБыстроНизкаяНовый стандарт

Когда использовать какой метод

// Простые объекты (объекты с примитивными значениями)
// Используй: JSON.parse(JSON.stringify(obj)) или structuredClone

const simple = { a: 1, b: 2, c: 3 };
const copy = structuredClone(simple);

// Объекты с функциями или сложными типами
// Используй: рекурсивное клонирование

const complex = {
  name: 'User',
  format: () => 'User Name',
  date: new Date()
};
const copy = deepClone(complex);

// Большие объекты (производительность критична)
// Используй: поверхностное клонирование или Immer

const large = { /* millions of properties */ };
const copy = { ...large }; // Только первый уровень

Лучшие практики

  1. Используй structuredClone — это новый стандарт
  2. Избегай JSON.parse/stringify для объектов с функциями
  3. Для React используй Immer — библиотека для иммутабельных обновлений
  4. Помни о производительности — глубокое клонирование может быть медленным
  5. Документируй — что именно должно копироваться

Пример с Immer (рекомендуется для React)

import produce from 'immer';

const user = {
  name: 'Иван',
  address: { city: 'Москва' }
};

const updated = produce(user, draft => {
  draft.address.city = 'Казань'; // Работаем со "черновиком"
  // Immer автоматически создаст клон
});

console.log(user.address.city); // 'Москва'
console.log(updated.address.city); // 'Казань'