Как клонировать объект без spread оператора?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Почему клонирование объектов важно
В 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() - современный метод с поддержкой типов и глубокого клонирования. Для вложенных структур нужно глубокое клонирование, иначе изменения повлияют на оригинал.