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

Как клонировать объект без spread оператора?

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

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

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

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

Почему клонирование объектов важно

В JavaScript объекты передаются по ссылке. Если просто присвоить объект другой переменной, обе переменные будут указывать на один и тот же объект в памяти. Изменение одного повлияет на другое. Для избежания побочных эффектов часто нужен клон.

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

const original = { name: 'Иван', age: 28 };
const copy = original;

copy.name = 'Петр';

console.log(original.name); // 'Петр' - ИЗМЕНИЛСЯ ОРИГИНАЛ!
console.log(copy.name);     // 'Петр'

original === copy; // true - это один и тот же объект

Способы клонировать объект БЕЗ spread оператора

1. Object.assign()

Старый стандартный способ, поддерживает IE11+:

const original = { name: 'Мария', age: 25 };
const copy = Object.assign({}, original);

copy.name = 'Анна';

console.log(original.name); // 'Мария' - OK!
console.log(copy.name);     // 'Анна'

Object.assign копирует все свойства первого уровня. С несколькими объектами:

const defaults = { color: 'blue', size: 'large' };
const userSettings = { color: 'red' };
const merged = Object.assign({}, defaults, userSettings);
// { color: 'red', size: 'large' }

2. JSON методы (для простых объектов)

Быстрый способ для данных без функций и методов:

const original = { name: 'Дмитрий', age: 30, tags: ['js', 'react'] };
const copy = JSON.parse(JSON.stringify(original));

copy.name = 'Сергей';
copy.tags.push('node');

console.log(original.name);  // 'Дмитрий'
console.log(original.tags);  // ['js', 'react']

Удобен для глубокого клонирования (вложенные объекты), но имеет ограничения:

  • Потеряются функции
  • undefined станет null
  • Date объекты станут строками
  • Set, Map и другие типы будут потеряны
// ❌ JSON способ не подходит для этого
const user = {
  name: 'Владимир',
  createdAt: new Date(),
  validate: function() { return true; }
};

const copy = JSON.parse(JSON.stringify(user));
console.log(copy.createdAt); // "2026-04-02T..." - строка, не Date!
console.log(copy.validate);  // undefined - функция потеряна

3. Object.create() + цикл

Нижний уровень, больше контроля:

const original = { a: 1, b: 2, c: 3 };
const copy = Object.create(Object.getPrototypeOf(original));

for (const key in original) {
  if (original.hasOwnProperty(key)) {
    copy[key] = original[key];
  }
}

copy.a = 10;
console.log(original.a); // 1

4. Object.keys() + reduce

Функциональный подход:

const original = { name: 'Павел', age: 28, city: 'Москва' };

const copy = Object.keys(original).reduce((acc, key) => {
  acc[key] = original[key];
  return acc;
}, {});

copy.name = 'Олег';
console.log(original.name); // 'Павел'

5. Деструктуризация в функции

function cloneObject(obj) {
  return { ...obj }; // Это spread, но можем обойтись без
}

// Без spread - через for...in
function cloneObject(obj) {
  const cloned = {};
  for (const key in obj) {
    cloned[key] = obj[key];
  }
  return cloned;
}

const original = { x: 1, y: 2 };
const copy = cloneObject(original);
copy.x = 5;
console.log(original.x); // 1

6. structuredClone() - современный способ

Новый метод с поддержкой Date, Map, Set:

const original = {
  name: 'Анатолий',
  createdAt: new Date(),
  tags: ['dev', 'js'],
  settings: { theme: 'dark' }
};

const copy = structuredClone(original);

copy.name = 'Борис';
copy.tags.push('typescript');

console.log(original.name);  // 'Анатолий'
console.log(original.tags);  // ['dev', 'js']
console.log(copy.createdAt instanceof Date); // true

Работает и для глубокого клонирования вложенных структур.

Глубокое vs Поверхностное клонирование

Поверхностное (Shallow)

const original = {
  name: 'Киrill',
  skills: ['JavaScript', 'React']
};

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

copy.skills.push('TypeScript');

console.log(original.skills); // ['JavaScript', 'React', 'TypeScript'] - ИЗМЕНИЛСЯ!
// Потому что skills - это ссылка на массив

Глубокое (Deep)

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

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

const original = {
  user: { name: 'Геннадий', age: 45 },
  skills: ['C++', 'Python', { level: 'senior' }]
};

const copy = deepClone(original);
copy.user.name = 'Виктор';
copy.skills[2].level = 'junior';

console.log(original.user.name);    // 'Геннадий'
console.log(original.skills[2].level); // 'senior'

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

const obj = { a: 1, b: { c: 2 }, d: new Date() };

// Object.assign - поверхностное
const copy1 = Object.assign({}, obj);
copy1.b.c = 999; // Повлияет на original! Опасно

// JSON.parse/stringify - глубокое, но теряет типы
const copy2 = JSON.parse(JSON.stringify(obj));
copy2.d instanceof Date; // false - потеря типа

// structuredClone - глубокое, сохраняет типы
const copy3 = structuredClone(obj);
copy3.b.c = 999;    // original не изменится
copy3.d instanceof Date; // true - тип сохранён

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

Выбор метода по ситуации

// Простой объект без вложений - Object.assign
const userCopy = Object.assign({}, user);

// Вложенные структуры - structuredClone (если поддерживается)
const configCopy = structuredClone(config);

// Старые браузеры - JSON способ (если нет Date/функций)
const dataCopy = JSON.parse(JSON.stringify(data));

// Максимальный контроль - рекурсивная функция
const treeCopy = deepClone(complexTree);

Выводы

Без spread оператора есть много способов клонировать объект. Object.assign() хорош для поверхностного копирования и хорошей поддержки браузерами. JSON.parse(stringify) работает для глубокого клонирования простых данных. structuredClone() - современный метод с поддержкой типов и глубокого клонирования. Для вложенных структур нужно глубокое клонирование, иначе изменения повлияют на оригинал.