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

Какой есть нюанс при копировании объекта через Object.assign?

2.2 Middle🔥 152 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Ключевой нюанс Object.assign: поверхностное копирование (shallow copy)

Основной и самый критичный нюанс Object.assign() заключается в том, что он выполняет поверхностное копирование, а не глубокое (deep copy). Это означает, что если объект содержит вложенные объекты или массивы, они копируются по ссылке, а не по значению.

Техническая демонстрация проблемы

const original = {
  name: 'Иван',
  address: {
    city: 'Москва',
    street: 'Тверская'
  },
  hobbies: ['чтение', 'спорт']
};

// Поверхностное копирование через Object.assign
const copy = Object.assign({}, original);

// Изменяем вложенный объект в копии
copy.address.city = 'Санкт-Петербург';
copy.hobbies.push('программирование');

console.log(original.address.city); // 'Санкт-Петербург' - ИЗМЕНИЛОСЬ!
console.log(original.hobbies); // ['чтение', 'спорт', 'программирование'] - ИЗМЕНИЛОСЬ!

Другие важные нюансы

1. Копирование только перечисляемых собственных свойств

Object.assign() копирует только те свойства, которые являются:

  • Собственными (не унаследованными от прототипа)
  • Перечисляемыми (enumerable: true)
const proto = { inheritedProp: 'из прототипа' };
const obj = Object.create(proto);
obj.ownEnumerable = 'собственное перечисляемое';
Object.defineProperty(obj, 'ownNonEnumerable', {
  value: 'собственное неперечисляемое',
  enumerable: false
});

const copy = Object.assign({}, obj);
console.log(copy.inheritedProp); // undefined
console.log(copy.ownEnumerable); // 'собственное перечисляемое'
console.log(copy.ownNonEnumerable); // undefined

2. Перезапись свойств при совпадении имён

Свойства копируются последовательно, и при совпадении имён более поздние значения перезаписывают предыдущие:

const target = { x: 1, y: 2 };
const source1 = { y: 10, z: 20 };
const source2 = { y: 100, z: 200 };

const result = Object.assign(target, source1, source2);
console.log(result); // { x: 1, y: 100, z: 200 }
console.log(target); // { x: 1, y: 100, z: 200 } - исходный target изменился!

3. Изменение целевого объекта

Первый аргумент (target) мутируется и возвращается. Это может быть неочевидно:

const original = { a: 1 };
const source = { b: 2 };

// Возвращаемое значение - тот же объект, что и original
const returned = Object.assign(original, source);

console.log(original === returned); // true
console.log(original); // { a: 1, b: 2 } - original ИЗМЕНЁН!

4. Не копируются геттеры и сеттеры

Object.assign() копирует значения свойств, а не дескрипторы свойств:

const source = {
  get currentYear() {
    return new Date().getFullYear();
  },
  regularProp: 'обычное свойство'
};

const copy = Object.assign({}, source);
console.log(typeof copy.currentYear); // 'number' (значение), а не 'function'
console.log(copy.currentYear); // 2024 (или текущий год), но это просто число

Решения для глубокого копирования

1. Рекурсивная реализация

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof Array) return obj.map(item => deepClone(item));
  
  const cloned = {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key]);
    }
  }
  return cloned;
}

2. Использование JSON методов (с ограничениями)

const deepCopy = JSON.parse(JSON.stringify(original));
// Не копирует функции, undefined, символы, циклические ссылки

3. Современные подходы

// ES2022: structuredClone() для глубокого копирования
const deepCopy = structuredClone(original);

// Использование спред-оператора для поверхностного копирования
const shallowCopy = { ...original }; // Аналогично Object.assign({}, original)

Когда использовать Object.assign?

Подходящие случаи:

  • Копирование плоских объектов без вложенностей
  • Слияние конфигурационных объектов
  • Создание неглубоких иммутабельных обновлений в Redux-редьюсерах

Неподходящие случаи:

  • Работа с глубоко вложенными структурами данных
  • Копирование объектов с методами (функциями)
  • Работа с специальными типами (Date, Map, Set, RegExp)

Вывод

Object.assign() — эффективный инструмент для поверхностного копирования, но критически важно понимать его ограничения. В современных проектах часто предпочитают использовать спред-оператор (...) для поверхностного копирования из-за его более чистого синтаксиса, а для глубокого копирования — structuredClone() или специализированные библиотеки типа Lodash (_.cloneDeep()). Понимание этого нюанса помогает избежать тонких багов, связанных с непреднамеренным мутированием исходных данных.