Найти в Дзене

Поверхностное и глубокое копирование в JavaScript

Народ, всем привет. Работа с объектами и массивами в JavaScript может вызывать путаницу, особенно когда речь заходит о копировании данных. Новички часто сталкиваются с тем, когда изменения в одной переменной неожиданно отражаются в другой. Чтобы разобраться, важно понимать разницу между поверхностным (shallow copy) и глубоким копированием (deep copy). Сегодня постараемся углубиться «внутрь», так сказать, и начнем мы с самого простого. Поехали. В JavaScript есть два основных типа данных: let a = 5; let b = a; // копируется значение b = 10; console.log(a); // 5 let obj1 = { name: "Alex" }; let obj2 = obj1; // копируется ссылка obj2.name = "Olga"; console.log(obj1.name); // "Olga" Это и становится причиной того, что простое присваивание объекта не создает его копию, а только указывает на ту же область памяти. Поверхностная копия создает новый объект, но копирует только верхний уровень свойств. Если свойство является вложенным объектом, копируется лишь ссылка на него. Существует несколько
Оглавление

Народ, всем привет. Работа с объектами и массивами в JavaScript может вызывать путаницу, особенно когда речь заходит о копировании данных. Новички часто сталкиваются с тем, когда изменения в одной переменной неожиданно отражаются в другой. Чтобы разобраться, важно понимать разницу между поверхностным (shallow copy) и глубоким копированием (deep copy). Сегодня постараемся углубиться «внутрь», так сказать, и начнем мы с самого простого. Поехали.

1. Ссылки и примитивы

В JavaScript есть два основных типа данных:

  • Примитивы, это числа, строки, boolean, null, undefined, symbol, bigint, которые хранятся по значению.
let a = 5;
let b = a; // копируется значение
b = 10;
console.log(a); // 5
-2
  • И объекты (массивы, функции, объекты), и они хранятся по ссылке.
let obj1 = { name: "Alex" };
let obj2 = obj1; // копируется ссылка
obj2.name = "Olga";
console.log(obj1.name); // "Olga"

Это и становится причиной того, что простое присваивание объекта не создает его копию, а только указывает на ту же область памяти.

2. Поверхностное копирование

Поверхностная копия создает новый объект, но копирует только верхний уровень свойств. Если свойство является вложенным объектом, копируется лишь ссылка на него. Существует несколько методов поверхностного копирования:

  • Object.assign()
let user = { name: "Ivan", address: { city: "Moscow" } };
let copy = Object.assign({}, user);
copy.name = "Petr";
console.log(user.name); // Ivan
copy.address.city = "Kazan";
console.log(user.address.city); // "Kazan"

Верхний уровень (name) скопирован, но вложенный объект address остался общим.

-3
  • Оператор spread (...)
let user = { name: "Ivan", address: { city: "Moscow" } };
let copy = { ...user };
copy.name = "Petr"; // не влияет на user
copy.address.city = "Kazan"; // меняет и в user
  • slice / concat для массивов
let arr = [1, 2, [3, 4]];
let copy = arr.slice();
copy[0] = 99; // не влияет на arr
copy[2][0] = 100; // влияет на arr

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

-4
Хотите знать больше? Читайте нас в нашем Telegram – там еще больше интересного: новинки гаджетов, технологии, AI, фишки программистов, примеры дизайна и маркетинга.

3. Глубокое копирование

Глубокое копирование создает полностью независимую копию объекта вместе со всеми вложенными объектами. Изменения в копии никак не повлияют на оригинал. Способы глубокого копирования:

  • JSON.parse(JSON.stringify(obj))
let user = { name: "Ivan", address: { city: "Moscow" } };
let deepCopy = JSON.parse(JSON.stringify(user));
deepCopy.address.city = "Kazan";
console.log(user.address.city); // "Moscow"

Важно: не копирует функции, теряются Date, Set, Map, undefined, символы и может быть неэффективным для больших структур.

  • structuredClone (современный стандарт)
let user = { name: "Ivan", address: { city: "Moscow" } };
let deepCopy = structuredClone(user);
deepCopy.address.city = "Kazan";
console.log(user.address.city); // "Moscow"

Он уже поддерживает Date, RegExp, Map, Set, такой некий более корректный и универсальный метод. Но работает не во всех старых браузерах.

-5

Есть еще библиотеки, типа Lodash: _.cloneDeep(obj), хорошо подходит для проектов, где часто требуется глубокое копирование. Можно сделать ручное рекурсивное копирование или написать собственную функцию, проходящую по всем уровням объекта:

function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
let copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
let original = { a: 1, b: { c: 2 } };
let clone = deepClone(original);
clone.b.c = 99;
console.log(original.b.c); // 2
-6

Что по итогу?

Поверхностное копирование используйте, когда объект плоский или вас устраивает совместное использование вложенных объектов. Например, для обновления состояния в React с неизменяемыми структурами. Глубокое копирование необходимо, если нужно полностью изолировать данные, например, при работе с состоянием в сложных приложениях или для клонирования конфигураций, которые не должны меняться. А также когда объект многоуровневый и важна независимость всех вложенных элементов.

По факту, если нужно просто скопировать объект «как есть» без вложенности, используйте spread или Object.assign. Для сложных объектов в современных браузерах подойдет structuredClone. А вот если проект использует Lodash, то лучше сразу применять _.cloneDeep. Для сериализуемых данных можно использовать JSON.stringify/parse.

-7

Если Вам нравятся наши статьи, и вы хотите отблагодарить автора (на развитие канала), нам будет очень приятно!