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

Почему в JavaScript есть проблема с клонированием объекта?

1.2 Junior🔥 141 комментариев
#JavaScript Core

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

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

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

Проблема клонирования объектов в JavaScript

Основная проблема: ссылки вместо значений

В JavaScript объекты хранятся по ссылке, а не по значению. Когда вы присваиваете объект другой переменной, вы копируете не сам объект, а ссылку на него в памяти. Это приводит к неожиданному поведению.

const original = { name: 'John', age: 30 };
const copy = original;

copy.name = 'Jane';
console.log(original.name); // 'Jane' — original изменился!

Это происходит потому, что copy и original указывают на один и тот же объект в памяти.

Поверхностное копирование (Shallow Copy)

Способы создать неглубокую копию объекта:

// Способ 1: Object.assign()
const copy = Object.assign({}, original);

// Способ 2: Spread оператор
const copy = { ...original };

// Способ 3: Object.create()
const copy = Object.create(Object.getPrototypeOf(original));
Object.assign(copy, original);

Но это решение работает только на первом уровне вложенности!

const original = {
  name: 'John',
  address: { city: 'Moscow', zip: '123456' }
};

const copy = { ...original };
copy.address.city = 'SPB';
console.log(original.address.city); // 'SPB' — вложенный объект изменился!

Почему? Потому что address — это объект, и в copy.address хранится ссылка на тот же самый объект address.

Глубокое копирование (Deep Copy)

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

  1. JSON способ (простой, но с ограничениями)
const deepCopy = JSON.parse(JSON.stringify(original));

Ограничения:

  • Теряются undefined, функции, символы
  • Дата становится строкой
  • Circular references вызывают ошибку
  • Map, Set, и другие специальные объекты теряют свой тип
  1. Рекурсивное копирование
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj.getTime());
  if (obj instanceof Array) return obj.map(item => deepClone(item));
  if (obj instanceof Object) {
    const clone = {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        clone[key] = deepClone(obj[key]);
      }
    }
    return clone;
  }
}
  1. Использование библиотек
import _ from 'lodash';
const deepCopy = _.cloneDeep(original);

Проблемы с циклическими ссылками

const obj = { name: 'John' };
obj.self = obj; // циклическая ссылка

// JSON.parse(JSON.stringify(obj)); // ERROR!

Для решения нужна специальная обработка:

function deepCloneWithCircular(obj, seen = new WeakMap()) {
  if (seen.has(obj)) return seen.get(obj);
  if (obj === null || typeof obj !== 'object') return obj;
  
  const clone = Array.isArray(obj) ? [] : {};
  seen.set(obj, clone);
  
  for (let key in obj) {
    clone[key] = deepCloneWithCircular(obj[key], seen);
  }
  return clone;
}

В React это особенно важно

В React нельзя мутировать state напрямую:

// ПЛОХО
state.user.name = 'Jane';
setState(state);

// ХОРОШО
setState({ ...state, user: { ...state.user, name: 'Jane' } });

Практические рекомендации

  1. Используй spread оператор для простых объектов
  2. Для вложенных структур — JSON.stringify (если подходит) или специальные функции
  3. Используй библиотеки вроде lodash для сложных случаев
  4. В React используй Immer.js для удобного работания с immutable updates
  5. Помни о циклических ссылках при написании собственных функций клонирования

Это одна из самых частых ошибок в JavaScript, поэтому понимание разницы между ссылками и значениями критически важно для писания безошибочного кода.