Как склонировать объект? assign, spread-оператор, JSON.parse(JSON.stringify), Object.create, рекурсивный обход structuredClone.

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#JavaScript #Объекты #База JS

Краткий ответ

Клонирование объектов в JavaScript — это создание копии объекта, которая не ссылается на исходный объект в памяти. Существует несколько способов клонирования, каждый из которых имеет свои особенности: поверхностное или глубокое копирование, поддержка различных типов данных, производительность.

Основные методы клонирования:

  • Object.assign() — поверхностное копирование
  • Spread-оператор — поверхностное копирование
  • JSON.parse(JSON.stringify()) — глубокое копирование с ограничениями
  • Object.create() — создание объекта с прототипом
  • Рекурсивный обход — полноценное глубокое копирование
  • structuredClone — современный метод глубокого копирования

Полный ответ

Клонирование объектов — важная концепция в JavaScript, позволяющая создавать независимые копии данных. Правильный выбор метода зависит от требований к глубине копирования и типов данных в объекте.

Методы поверхностного копирования

1. Object.assign()

Копирует только перечисляемые свойства первого уровня:

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

2. Spread-оператор

Современный синтаксис для поверхностного копирования:

const copy = { ...original };

Методы глубокого копирования

1. JSON.parse(JSON.stringify())

Простой способ глубокого копирования с ограничениями:

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

2. Рекурсивный обход

Полноценное глубокое копирование всех типов данных:

function deepClone(obj) {
  // Реализация рекурсивного копирования
}

3. structuredClone

Современный API браузера для глубокого копирования:

const deepCopy = structuredClone(original);

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

Пример с вложенными объектами

const user = {
  name: 'Иван',
  address: {
    city: 'Москва',
    street: 'Ленина'
  }
};
 
// Поверхностное копирование
const shallow = { ...user };
shallow.address.city = 'Санкт-Петербург';
// Исходный объект тоже изменится!
 
// Глубокое копирование
const deep = structuredClone(user);
deep.address.city = 'Казань';
// Исходный объект не изменится

Пример с массивами

const data = {
  items: [1, 2, { value: 3 }]
};
 
// Разные методы дают разные результаты
const assignCopy = Object.assign({}, data);
const spreadCopy = { ...data };
const jsonCopy = JSON.parse(JSON.stringify(data));

Распространенные ошибки

1. Неправильный выбор метода

// ❌ Использование spread для глубокого копирования
const config = {
  database: {
    host: 'localhost',
    port: 5432
  }
};
 
const copy = { ...config };
copy.database.host = 'remote'; // Изменит и исходный объект!
 
// ✅ Использование structuredClone для глубокого копирования
const deepCopy = structuredClone(config);

2. Использование JSON-методов с несовместимыми типами

// ❌ JSON-методы не работают с функциями, undefined, Symbol
const obj = {
  func: () => {},
  undef: undefined,
  sym: Symbol('test'),
  date: new Date()
};
 
const copy = JSON.parse(JSON.stringify(obj));
// Функции, undefined и Symbol будут потеряны!
 
// ✅ Использование structuredClone
const properCopy = structuredClone(obj);

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

  1. Используйте spread/Object.assign для простых объектов — когда нет вложенных ссылок
  2. Применяйте structuredClone для сложных структур — современный и надежный метод
  3. Избегайте JSON-методов с функциями и специальными типами — они теряются при сериализации
  4. Пишите кастомные функции для специфичных случаев — когда нужен контроль над процессом
  5. Проверяйте поддержку structuredClone — для старых браузеров нужен полифилл

Совместимость

  • Object.assign — ES6, поддерживается всеми современными браузерами
  • Spread-оператор — ES6-ES9, зависит от типа данных
  • JSON-методы — поддерживаются всеми браузерами
  • Object.create — ES5, широкая поддержка
  • structuredClone — современные браузеры (Chrome 98+, Firefox 94+)
  • Рекурсивный обход — работает везде, реализация вручную

Ключевые особенности методов

  1. Поверхностное копирование — только первый уровень, вложенные объекты остаются ссылками
  2. Глубокое копирование — все уровни вложенности, полностью независимая копия
  3. Производительность — разные методы имеют разную скорость работы
  4. Поддержка типов — не все методы корректно работают с функциями, датами, RegExp
  5. Память — глубокое копирование требует больше ресурсов

Правильный выбор метода клонирования зависит от структур данных, требований к производительности и поддерживаемых браузеров.


Задача для проверки знаний

Задача

Какой будет результат и почему?

const original = {
  name: 'Тест',
  data: {
    value: 42,
    nested: {
      flag: true
    }
  },
  tags: ['js', 'clone'],
  func: function() { return this.name; }
};
 
// Метод 1
const copy1 = { ...original };
 
// Метод 2
const copy2 = JSON.parse(JSON.stringify(original));
 
// Метод 3
const copy3 = structuredClone(original);
 
// Что будет при изменении?
copy1.data.value = 100;
copy2.tags[0] = 'javascript';
copy3.data.nested.flag = false;
 
console.log(original.data.value);
console.log(original.tags[0]);
console.log(original.data.nested.flag);
console.log(copy2.func);
Посмотреть ответ

Ответ:

100
javascript
false
undefined

Объяснение:

  1. copy1 (spread-оператор):

    • Поверхностное копирование
    • copy1.data остается ссылкой на original.data
    • Изменение copy1.data.value влияет на original.data.value
    • original.data.value становится 100
  2. copy2 (JSON-методы):

    • Глубокое копирование, но с ограничениями
    • copy2.tags — независимая копия массива
    • Изменение copy2.tags[0] не влияет на original.tags[0]
    • original.tags[0] остается ‘js’
    • copy2.func будет undefined, так как функции теряются при JSON-сериализации
  3. copy3 (structuredClone):

    • Полноценное глубокое копирование
    • copy3.data.nested — независимая копия
    • Изменение copy3.data.nested.flag не влияет на original.data.nested.flag
    • original.data.nested.flag остается true

Ошибка в анализе выше — пересмотрим:

На самом деле:

  • original.data.value будет 100 (изменено через copy1)
  • original.tags[0] останется ‘js’ (copy2.tags — независимая копия)
  • original.data.nested.flag останется true (copy3.data.nested — независимая копия)
  • copy2.func будет undefined (функции теряются в JSON)

Правильный результат:

100
js
true
undefined

Этот пример демонстрирует важные различия между методами клонирования:

  • Поверхностное копирование сохраняет ссылки на вложенные объекты
  • JSON-методы теряют функции, undefined, Symbol и специальные объекты
  • structuredClone — наиболее полный метод, поддерживающий все типы данных

Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪