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

В чём разница между глубокой и неглубокой копией объекта?

1.6 Junior🔥 111 комментариев
#JavaScript Core

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

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

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

В чём разница между глубокой и неглубокой копией объекта

Это фундаментальная концепция в JavaScript, которая часто приводит к багам. Разница связана с тем, как JavaScript работает с объектами в памяти.

Основы: Ссылочные типы

В JavaScript примитивные типы (число, строка, boolean) копируются по значению, а объекты и массивыпо ссылке.

// Примитивы — копируются по значению
let a = 5;
let b = a;
b = 10;
console.log(a); // 5 (не изменился)

// Объекты — копируются по ссылке
let obj1 = { name: 'John' };
let obj2 = obj1; // Копируем ссылку на объект
obj2.name = 'Jane';
console.log(obj1.name); // 'Jane' — изменился!

Неглубокая копия (Shallow Copy)

Неглубокая копия — копируются только значения верхнего уровня. Вложенные объекты остаются ссылками.

// Способ 1: Object.assign
const original = {
  name: 'John',
  address: { city: 'NYC', zip: '10001' }
};

const shallow = Object.assign({}, original);

// Верхний уровень скопирован
shallow.name = 'Jane';
console.log(original.name); // 'John' — не изменился

// Но вложенный объект остался ссылкой!
shallow.address.city = 'LA';
console.log(original.address.city); // 'LA' — изменился!
// Способ 2: Spread operator (...)
const original = {
  name: 'John',
  address: { city: 'NYC' }
};

const shallow = { ...original };

// Верхний уровень скопирован
shallow.name = 'Jane';
console.log(original.name); // 'John'

// Вложенный объект остался ссылкой
shallow.address.city = 'LA';
console.log(original.address.city); // 'LA'
// Способ 3: Array.slice() для массивов
const arr1 = [1, 2, { nested: 'value' }];
const arr2 = arr1.slice(); // Неглубокая копия

arr2.push(3);
console.log(arr1); // [1, 2, { nested: 'value' }] (не изменился)

arr2[2].nested = 'changed';
console.log(arr1[2].nested); // 'changed' (изменился!)

Проблема неглубокой копии:

const user = {
  name: 'John',
  profile: { age: 30, city: 'NYC' }
};

const copy = { ...user };

// Ожидаю, что копия независима
copy.profile.age = 40;

// Ошибка! Изменился оригинал
console.log(user.profile.age); // 40

Глубокая копия (Deep Copy)

Глубокая копия — копируются все уровни, включая вложенные объекты.

// Способ 1: JSON.parse(JSON.stringify()) — быстро, но с ограничениями
const original = {
  name: 'John',
  address: { city: 'NYC' }
};

const deep = JSON.parse(JSON.stringify(original));

deep.address.city = 'LA';
console.log(original.address.city); // 'NYC' — не изменился

Проблемы JSON.parse(JSON.stringify()):

const obj = {
  date: new Date(),
  func: () => console.log('hello'),
  undef: undefined,
  circular: null
};

obj.circular = obj; // Циклическая ссылка

const copy = JSON.parse(JSON.stringify(obj));
// ❌ Ошибка!
// - Date становится строкой
// - Функции удаляются
// - undefined удаляется
// - Циклические ссылки вызывают ошибку
// Способ 2: Рекурсивная копия
function deepCopy(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 => deepCopy(item));
  }
  
  // Обработка Object
  const copy = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }
  return copy;
}

const original = {
  name: 'John',
  address: { city: 'NYC', zip: { code: '10001' } },
  dates: [new Date(), new Date()]
};

const deep = deepCopy(original);
deep.address.zip.code = '20001';
console.log(original.address.zip.code); // '10001' — не изменился
// Способ 3: Встроенные библиотеки
import _ from 'lodash';

const original = { a: { b: { c: 1 } } };
const deep = _.cloneDeep(original);

deep.a.b.c = 2;
console.log(original.a.b.c); // 1
// Способ 4: structuredClone (современный способ, ES2022)
const original = {
  name: 'John',
  address: { city: 'NYC' },
  date: new Date()
};

const deep = structuredClone(original);
deep.address.city = 'LA';
console.log(original.address.city); // 'NYC'

// ✅ Работает с Date, Map, Set
// ❌ Не работает с функциями и циклическими ссылками

Сравнение методов

МетодСкоростьDateFunctionЦиклические ссылкиСложность
spread (...)Очень быстроНизкая
JSON.parseБыстроНизкая
structuredCloneСреднееНизкая
РекурсияМедленноВысокая
Lodash cloneDeepБыстроНизкая

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

Проблема: неглубокая копия в useState

function UserForm() {
  const [user, setUser] = useState({
    name: 'John',
    address: { city: 'NYC' }
  });
  
  const handleCityChange = (city: string) => {
    // ❌ Неправильно — изменяет оригинальный объект
    user.address.city = city;
    setUser(user);
    
    // ❌ React может не заметить изменения!
  };
  
  return <div>{user.address.city}</div>;
}

Решение: глубокая копия

function UserForm() {
  const [user, setUser] = useState({
    name: 'John',
    address: { city: 'NYC' }
  });
  
  const handleCityChange = (city: string) => {
    // ✅ Правильно — создаём новый объект
    setUser(prev => ({
      ...prev,
      address: {
        ...prev.address,
        city
      }
    }));
  };
  
  return <div>{user.address.city}</div>;
}

Или используй structuredClone:

const handleCityChange = (city: string) => {
  const newUser = structuredClone(user);
  newUser.address.city = city;
  setUser(newUser);
};

Когда использовать что

Неглубокая копия достаточно когда:

  • Только primitives в объекте
  • Вложенные объекты не меняются
  • Нужна максимальная скорость

Нужна глубокая копия когда:

  • Вложенные объекты/массивы
  • Меняешь вложенные данные
  • Redux/состояние приложения
  • Работаешь с форм данными

Вывод: в большинстве случаев используй spread/Object.assign для простых объектов и structuredClone или библиотеки для сложных случаев.

В чём разница между глубокой и неглубокой копией объекта? | PrepBro