Присвоєння за посиланням і за значенням
Фундаментальною відмінністю складних типів від примітивів є те, як вони зберігаються і копіюються. Примітиви: рядки
, числа
, булі
, null
і undefined
, під час присвоєння повністю копіюються за значенням (by value).
Зі складними типами - все по-іншому. У змінній, якій присвоєно масив або об'єкт, зберігається не саме значення, а адреса його місця в пам'яті, іншими словами - посилання (вказівник) на нього і вони передаються за посиланням (by reference).
Уявімо змінну у вигляді аркушу паперу. Її значення ми уявимо як запис на цьому аркуші.
Якщо ми захочемо повідомити зміст цього запису користувачам, то можемо це зробити наступним чином - зробити фізичні копії і вручити кожному, тобто зробити багато незалежних копій (присвоєння за значенням).
Або покласти аркуш в зачиненій кімнаті і дати користувачам ключ від цієї кімнати, тобто один екземпляр із загальним доступом (присвоєння за посиланням).
Тепер змінимо дані на аркуші паперу - значення змінної. Очевидно, що відвідувачі кімнати завжди будуть бачити зміни, які ми вносимо, оскільки змінюється оригінал і вони мають до нього доступ. І також очевидно, що власники паперових копій не помітять змін, дивлячись на свої копії.
За умови передачі за значенням, змінним виділяється нова комірка пам'яті і в неї копіюються дані. Аналогія з багатьма копіями паперового аркушу має цілком реальне втілення, окремий аркуш для кожної копії.
За умови передачі за посиланням, замість створення нового об'єкта, змінній присвоюється посилання (вказівник) на вже існуючий об'єкт, тобто на його місце в пам'яті. Таким чином, декілька змінних можуть вказувати на один і той самий об'єкт, за аналогією із закритою кімнатою, вони мають ключ доступу до оригіналу аркушу.
Усі примітивні типи присвоюються за значенням, тобто створюється копія.
let a = 5;
// Присвоєння за значенням, в пам'яті буде створена ще
// одна комірка, в яку буде скопійоване значення 5
let b = a;
console.log(a); // 5
console.log(b); // 5
// Змінимо значення a
a = 10;
console.log(a); // 10
// Значення b не змінилося, оскільки це окрема копія
console.log(b); // 5
Складні типи - об'єкти, масиви, функції присвоюються за посиланням, тобто змінна просто отримує посилання на вже існуючий об'єкт.
const a = ["Mango"];
// Оскільки a - це масив, в b записується посилання на вже існуючий
// масив в пам'яті. Тепер a і b вказують на той самий масив.
const b = a;
console.log(a); // ["Mango"]
console.log(b); // ["Mango"]
// Змінимо масив, додавши ще один елемент, використовуючи вказівник з a
a.push("Poly");
console.log(a); // ["Mango", "Poly"]
// b також змінилось, тому що b, як і a,
// просто містить посилання на те ж саме місце в пам'яті
console.log(b); // ["Mango", "Poly"]
// Результат повторюється
b.push("Ajax");
console.log(a); // ["Mango", "Poly", "Ajax"]
console.log(b); // ["Mango", "Poly", "Ajax"]