Передача за значенням і за посиланням
«За значенням» означає, що змінна зберігає саме значення, і під час копіювання створюється нова незалежна копія даних; «за посиланням» означає, що змінна зберігає лише вказівник на місце в пам'яті, і копіюється саме цей вказівник. У JavaScript за значенням поводяться примітиви, а за посиланням, об'єкти та всі складні структури даних.
Теорія
TL;DR
- Примітиви (
number,string,boolean,null,undefined,symbol,bigint) передаються за значенням. - Об'єкти (
object,array,function,Date,Map,Setтощо) передаються за посиланням. - Копія примітива повністю незалежна: зміна
bне впливає наa. - Копія об'єкта, це та сама структура в пам'яті: зміна через одну змінну видно через іншу.
- Щоб отримати незалежний об'єкт, потрібна поверхнева (
{ ...obj }) або глибока копія.
Швидкий приклад
// за значенням
let a = 5;
let b = a; // копіюється значення 5
b = 10;
console.log(a); // 5, не змінився
console.log(b); // 10
// за посиланням
const user1 = { name: 'Maria' };
const user2 = user1; // копіюється ПОСИЛАННЯ, а не сам об'єкт
user2.name = 'Alex';
console.log(user1.name); // "Alex"
console.log(user2.name); // "Alex"Що означає «за значенням» (by value)
Змінна зберігає саме значення, а не посилання на нього. Під час копіювання створюється нова незалежна копія даних.
За значенням передаються примітиви (прості типи даних). До них належать:
numberstringbooleannullundefinedsymbolbigint
let a = 5;
let b = a; // копіюється значення 5
b = 10;
console.log(a); // 5 <- не змінився
console.log(b); // 10a і b тепер два незалежні значення, і зміна b не впливає на a.
Що означає «за посиланням» (by reference)
Змінна не зберігає сам об'єкт, вона зберігає посилання (вказівник) на місце в пам'яті, де цей об'єкт лежить.
За посиланням передаються всі об'єкти та складні структури даних:
objectarrayfunctionDate,Map,Setі так далі
const user1 = { name: 'Maria' };
const user2 = user1; // копіюється ПОСИЛАННЯ, а не сам об'єкт
user2.name = 'Alex';
console.log(user1.name); // "Alex"
console.log(user2.name); // "Alex"Обидві змінні вказують на один і той самий об'єкт у пам'яті, тому зміна user2 змінює і user1.
Візуально це виглядає так:
+------------+
| user1 | --+
+------------+ |
v
{ name: "Maria" }
^
+------------+ |
| user2 | --+
+------------+Приклад з масивом
Масив, це теж об'єкт, тож для нього діє те саме правило:
const arr1 = [1, 2, 3];
const arr2 = arr1;
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [1, 2, 3, 4]Обидва масиви, це один і той самий об'єкт у пам'яті.
Як створити копію об'єкта і не тягнути посилання
Якщо потрібен новий незалежний об'єкт, дані треба явно скопіювати.
Поверхнева копія (spread):
const user1 = { name: 'Tim', age: 25 };
const user2 = { ...user1 }; // оператор spread
user2.name = 'Alex';
console.log(user1.name); // "Tim"
console.log(user2.name); // "Alex"Глибока копія (для вкладених об'єктів):
const user1 = { name: 'Tim', address: { city: 'Kyiv' } };
const user2 = JSON.parse(JSON.stringify(user1)); // глибоке клонування
user2.address.city = 'Lviv';
console.log(user1.address.city); // "Kyiv"
console.log(user2.address.city); // "Lviv"Стек, купа і підсумкові таблиці
| Тип | Де зберігається | Що відбувається під час копіювання |
|---|---|---|
| Примітив (by value) | у стеку (stack) | створюється нова копія |
| Об'єкт (by reference) | у купі (heap) | копіюється посилання на те саме місце |
| Тип даних | Передача | Приклад |
| --- | --- | --- |
string, number, boolean, null, undefined, symbol, bigint | за значенням | let a = 5; let b = a; |
object, array, function, Date, Map, Set | за посиланням | let obj2 = obj1; |
Коротко:
- За значенням створюється копія даних.
- За посиланням копіюється вказівник на той самий об'єкт.
- Саме тому зміна одного об'єкта впливає на інший, якщо вони пов'язані посиланням.
Типові помилки
- Вважати, що
constзахищає вміст об'єкта.constзабороняє перепризначити змінну, але не забороняє змінювати поля об'єкта, на який вона вказує. - Робити
const copy = originalдля об'єкта і думати, що це копія. Це друга назва для того самого об'єкта. - Плутати поверхневу копію з глибокою:
{ ...user }копіює лише верхній рівень, вкладені об'єкти залишаються спільними. - Використовувати
JSON.parse(JSON.stringify(obj))бездумно: цей спосіб втрачаєundefined, функції,Symbol, перетворюєDateна рядок і ламається на циклічних посиланнях. Для складних випадків кращеstructuredClone(obj). - Порівнювати об'єкти через
===і чекатиtrue:{ a: 1 } === { a: 1 }даєfalse, бо це два різні посилання.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.