Skip to main content

Передача аргументів за значенням і за посиланням

Коли ми передаємо аргументи у функцію, JavaScript копіює їх, а те, як саме копіює, залежить від типу даних: примітиви копіюються за значенням, а об'єкти, масиви і функції передаються за посиланням, точніше за копією посилання. Саме тому одні зміни всередині функції видно ззовні, а інші зникають разом із викликом.

Теорія

TL;DR

  • Примітиви (number, string, boolean, null, undefined, symbol, bigint) передаються за значенням: у параметр лягає повна копія.
  • Об'єкти, масиви і функції передаються за посиланням, тобто у параметр лягає копія посилання на ту саму ділянку пам'яті.
  • Зміна вмісту об'єкта всередині функції (obj.name = "Bob", arr.push(4)) видно в оригіналі.
  • Перепризначення параметра (obj = {...}, arr = [...]) оригінал не зачіпає, бо змінюється лише локальна копія посилання.
  • Строго кажучи, JavaScript завжди передає за значенням: для об'єктів цим значенням є посилання.

Швидкий приклад

javascript
let a = 10; function changeValue(x) { x = 20; // змінюємо копію } changeValue(a); console.log(a); // 10

Значення a не змінилося, бо у x записалася лише його копія.

Передача за значенням

Примітиви, тобто числа, рядки, булеві значення, null, undefined, symbol і bigint, копіюються повністю.

Тип данихЯк передається
Примітиви (числа, рядки, булеві, null, undefined, symbol, bigint)за значенням
Об'єкти, масиви, функціїза посиланням (точніше, за копією посилання)

Пояснення на прикладі вище:

  • У x записується копія значення a.
  • Зміна x жодним чином не впливає на a.

У пам'яті це виглядає так:

text
a -> 10 x -> 10 (копія)

Передача за посиланням

Об'єкти, масиви і функції не копіюються цілком. Замість цього передається посилання на їхню адресу в пам'яті.

javascript
const user = { name: "Alice" }; function rename(obj) { obj.name = "Bob"; } rename(user); console.log(user.name); // "Bob"

Пояснення:

  • Змінна user зберігає посилання на об'єкт у пам'яті.
  • У obj копіюється це саме посилання, тож обидві змінні вказують на один і той самий об'єкт.
  • Зміни через obj видно і через user.

У пам'яті це виглядає так:

text
user --+ v { name: "Bob" } ^ obj ---+

Перепризначення посилання всередині функції

Якщо всередині функції змінити саме посилання, оригінал залишиться недоторканим.

javascript
const user = { name: "Alice" }; function reassign(obj) { obj = { name: "Charlie" }; // нове посилання } reassign(user); console.log(user.name); // "Alice"

Що тут відбувається:

  • У obj спочатку копіюється посилання на user.
  • Потім obj перенаправляється на новий об'єкт, але оригінальне посилання (user) залишається тим самим.

Це і є доказ того, що передається копія посилання, а не сам слот змінної.

Приклад з масивом

Масив це теж об'єкт, тому діють ті самі правила. Зміна вмісту видно ззовні:

javascript
const numbers = [1, 2, 3]; function modify(arr) { arr.push(4); // змінюємо вміст } modify(numbers); console.log(numbers); // [1, 2, 3, 4]

А ось перепризначення посилання, ні:

javascript
function modify(arr) { arr = [9, 9, 9]; // створюємо новий масив } modify(numbers); console.log(numbers); // [1, 2, 3]

Підсумок і проста аналогія

Тип данихЯк передаєтьсяЧи впливає зміна всередині функції на оригінал?
Примітиви (number, string, boolean, null, undefined, symbol, bigint)За значеннямНі
Об'єкти, масиви, функціїЗа посиланням (копією посилання)Так, якщо змінюється вміст
Перепризначення параметра всередині функціїЛокальна копіяНі

Аналогія, яка добре лягає на співбесіді:

  • За значенням це як передати копію аркуша паперу: якщо ви щось напишете на своїй копії, оригінал не зміниться.
  • За посиланням це як передати адресу сейфа: обидва можете відкрити той самий сейф і змінити те, що всередині.

Типові помилки

  • Вважати, що JavaScript має справжню передачу за посиланням. Її немає: функція не може перепризначити змінну, яку їй передали. Правильне формулювання це «передача копії посилання», або, як кажуть англійською, call by sharing.
  • Плутати мутацію з перепризначенням. obj.name = "Bob" змінює спільний об'єкт, obj = {...} змінює лише локальний параметр.
  • Думати, що const захищає від змін. const user = {...} забороняє перепризначити user, але не забороняє змінювати його властивості всередині функції. Для захисту потрібен Object.freeze або копія.
  • Розраховувати, що поверхнева копія рятує повністю. { ...user } і structuredClone поводяться по-різному: спред копіює лише верхній рівень, тож вкладені об'єкти залишаються спільними.
  • Забувати, що рядок незмінний. Метод на кшталт str.toUpperCase() не змінює аргумент, а повертає новий рядок, який треба присвоїти.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.