
Когда вы работаете с объектами и массивами в JavaScript, может показаться, что они ведут себя странно: изменение одной переменной неожиданно влияет на другую. Все это — следствие работы ссылочных типов данных.
Привет, Хабр! Меня зовут Александр Дудукало, я автор базового курса по JavaScript. В этой статье я простыми словами расскажу, как работают ссылки, почему это важно знать и как правильно копировать объекты.
Важно: некоторые технические детали сознательно упрощены для лучшего понимания основ. Если вам удобнее изучать материал в видеоформате — смотрите ролик.
Ссылочный тип данных
По моему опыту, теория лучше усваивается на разборе примера или какой-то проблемы. Именно поэтому предлагаю сразу посмотреть на этот код:
const product1 = {
name: "Кофе",
price: 400,
};
const product2 = product1;
product2.price = 900;
// Товар 1
console.log(Стоимость ${product1.name}: ${product1.price});
// Товар 2
console.log(Стоимость ${product2.name}: ${product2.price});

Если внимательно его изучить, то нетрудно заметить, что после изменения стоимости второго товара, который был получен путем копирования объекта первого, изменилась стоимость и первого товара тоже. Отсюда вопрос: как так получается?
Ответом и является тема этой статьи: так работают ссылочные типы данных. А чтобы это стало совсем ясно, давайте посмотрим, как ведут себя примитивные типы данных:
let price1 = 400;
let price2 = price1;
price2 = 900;
console.log(price1);
console.log(price2);

Видите разницу? В первом случае изменение product2 задело product1, а во втором изменение price2 никак не повлияло на price1. В этом и заключается ключевое различие. А теперь перейдем к определению.
Ссылочный тип данных — это тип (объекты, массивы, функции) работа с которым в JavaScript происходит не напрямую, а через ссылку на место в памяти, где хранятся сами данные.
Когда вы присваиваете объект переменной, в нее записывается не сам объект, а «адрес», по которому он находится. Копируя эту переменную, вы копируете именно адрес, а не сам объект. В итоге несколько переменных могут ссылаться на один и тот же объект.
В показанном выше примере в константу product2 копируется ссылка на объект, а именно на тот же объект, который был создан при объявлении константы product1. То есть обе константы ссылаются на одни и те же данные.
Для ясности предлагаю посмотреть на схему:

Примитивные типы данных — это «простые» типы, которые хранят непосредственно свое значение. При копировании создается независимая копия этого значения.
К ним относятся:
number (числа) — 5, -10.5;
string (строки) — «Привет»;
boolean (логический тип) — true или false;
null — специальное значение «ничего»;
undefined — значение «не определено»;
symbol и bigint (используются реже).
При работе со ссылочными данными важно понимать, изменение данных через одну переменную приведет к изменению во всех других переменных, которые ссылаются на тот же объект. Это следствие того, что это одни и те же данные. И важно это знать, чтобы не нарушить логику работы программы.

Бесплатный базовый курс по JS
Рассказываем, как работать с переменными, типами данных, функциями и многом другом!
Копирование объектов
Итак, мы разобрались со ссылочными типами данных. И узнали, что при переприсваивании объекта копируется ссылка, а не сам объект. Но что делать, если все же нужно создать полную копию? Вот четыре самых популярных и полезных способа создать независимую копию объекта.
Поверхностное копирование через Spread оператор (...)
const product1 = {
name: "Кофе",
price: 400,
};
// Создаем реальную копию с помощью Spread оператора
const product2 = { ...product1 };
product2.price = 900;
console.log(product1.price);
console.log(product2.price);

Как это работает? Оператор ... раскрывает все свойства исходного объекта product1 и помещает их в новый объект. По сути, это короткий и элегантный способ скопировать все поля на одном уровне.
Но есть важное ограничение: это поверхностное копирование. Если у вас есть вложенные объекты, они скопируются по ссылке:
const product1 = {
name: "Кофе",
details: {
country: "Бразилия"
}
};
const product2 = { ...product1 };
product2.details.country = "Колумбия";
console.log(product1.details.country);

Поверхностное копирование через Object.assign()
Это классический метод, который отлично работает и сегодня:
const product1 = {
name: "Кофе",
price: 400,
};
// Копируем свойства product1 в новый пустой объект
const product2 = Object.assign({}, product1);
product2.price = 900;
console.log(product1.price);
console.log(product2.price);

Метод Object.assign() берет целевой объект (первый аргумент, у нас это пустой объект {}) и копирует в него свойства из всех последующих объектов (в нашем случае — из product1).
Но, как и Spread оператор, Object.assign() делает только поверхностную копию.
Глубокое копирование через JSON.parse(JSON.stringify())
const product1 = {
name: "Кофе",
price: 400,
details: {
country: "Бразилия"
}
};
// Создаем глубокую копию
const product2 = JSON.parse(JSON.stringify(product1));
product2.details.country = "Колумбия";
console.log(product1.details.country);
console.log(product2.details.country);

Этот прием состоит из двух шагов.
JSON.stringify (product1) — превращает объект в строку JSON.
JSON.parse (...) — превращает эту строку обратно в новый, полностью независимый объект.
Но есть важные ограничения. Например, этот метод игнорирует специальные типы данных, такие как undefined, Function и Symbol, а также не копирует методы объекта. Кроме того, он может сломаться, если в объекте есть циклические ссылки (когда объект ссылается сам на себя).
Глубокое копирование через structuredClone()
Помните ограничения JSON-метода? Разработчики JavaScript учли их и добавили в язык специальную функцию для глубокого копирования - structuredClone().
const product1 = {
name: "Кофе",
price: 400,
details: {
country: "Бразилия"
},
tags: ['ароматный', 'горячий']
};
// Создаем настоящую глубокую копию
const product2 = structuredClone(product1);
product2.details.country = "Колумбия";
product2.tags.push('свежий');
console.log(product1.details.country);
console.log(product1.tags);
console.log(product2.details.country);
console.log(product2.tags);

structuredClone() создает полную, можно сказать, «глубокую» копию объекта, рекурсивно копируя все вложенные объекты и массивы. Это его прямая задача.
Подводим итоги
Мы разобрали, что объекты в JavaScript хранятся и передаются по ссылке. Это значит, что при копировании obj2 = obj1 обе переменные работают с одними данными. Понимание ссылочной природы — это ключ к предотвращению ошибок. Ссылки экономят память, но могут неожиданно изменять данные в разных частях программы.
Если у вас возникли вопросы или сложности при изучении JavaScript, делитесь ими в комментариях — обсудим вместе. В следующих статьях будет еще больше полезных материалов и практических советов — еще увидимся.
Комментарии (7)

voltag
23.10.2025 13:41console.log(Стоимость ${product1.name}: ${product1.price});поставьте кавычки в нужные места, пожалуйста

Format-X22
23.10.2025 13:41Можно ещё сверх-легкую копию поверх.
const x = {a: 1, b: 2}; // просто объект const y = Object.create(x); // создаем слой сверху y.a = 100; // заменяем в слое переменную "a" на свою console.log(x.a, x.b, y.a, y.b); // 1, 2, 100, 2 // в оригинальном объекте всё также, в нашем новом слое - "a" замененаРаботает на прототипном движке под капотом JS, так работают классы внутри. В итоге и ссылки сохраняем, и меняем только то что нас интересует.
Ну и бонусом сверху - "delete y.a;" удалит переменную из слоя, но оставит оригинал. После такого делейта y.a начнет возвращать "1", также как в объекте "x". Ну и конечно любые изменение в "x" отобразятся в "y" если мы не перезаписали в "y" чем-то своим.
Слоев можно насоздавать любое нужное количество.

AntowaKartowa
23.10.2025 13:41Я вас удивлю, но все типы данных в JavaScript ссылочные. Об этом по сути говорит даже Дэн Абрамов в своём курсе Just JavaScript.
ovsale
вы весь учебник по js будете тут публиковать?