Как передаются параметры функций в JavaScript?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как передаются параметры функций в JavaScript
Передача параметров в JavaScript основана на принципе pass-by-value для примитивов и pass-by-reference для объектов. Понимание этого различия критично для избежания неожиданных ошибок.
1. Примитивные типы — передача по значению
Для примитивов (number, string, boolean, null, undefined, symbol, bigint) создаётся копия значения. Изменение параметра внутри функции не влияет на исходную переменную.
function modify(value) {
value = value * 2;
console.log('Inside function:', value); // 20
}
let num = 10;
modify(num);
console.log('Outside function:', num); // 10 (не изменилось)
Почему? JavaScript создаёт копию значения в стеке памяти:
До вызова: После вызова:
num = 10 num = 10 (в основном scope)
value = 10 (копия в scope функции)
value = 20 (изменение копии)
То же самое с строками и другими примитивами:
function changeString(str) {
str = str + ' modified';
return str;
}
let original = 'Hello';
let result = changeString(original);
console.log(original); // 'Hello' (не изменилось)
console.log(result); // 'Hello modified'
2. Объекты и массивы — передача по ссылке
Для объектов и массивов передаётся ссылка на объект, а не сама копия. Это означает, что обе переменные указывают на один и тот же объект в памяти.
function modifyObject(obj) {
obj.name = 'Jane'; // изменяем существующее свойство
console.log('Inside function:', obj); // { name: 'Jane', age: 30 }
}
let person = { name: 'John', age: 30 };
modifyObject(person);
console.log('Outside function:', person); // { name: 'Jane', age: 30 } (изменилось!)
Визуально:
person ──→ { name: 'John', age: 30 } (объект в памяти)
↑
obj (ссылка указывает на тот же объект)
После модификации:
person ──→ { name: 'Jane', age: 30 } (объект изменился)
↑
obj (та же ссылка)
3. Важный нюанс: переприсваивание ссылки
Если переприсвоить параметр (создать новую ссылку), это не влияет на исходную переменную:
function reassignObject(obj) {
obj = { name: 'New Object' }; // переприсваиваем на новый объект
console.log('Inside function:', obj); // { name: 'New Object' }
}
let original = { name: 'Original' };
reassignObject(original);
console.log('Outside function:', original); // { name: 'Original' } (не изменилось)
Почему не изменилось?
Внутри функции: obj = new Object()
obj теперь указывает на НОВЫЙ объект
original всё ещё указывает на СТАРЫЙ объект
4. Массивы — то же самое, что объекты
Массивы — это объекты, поэтому передаются по ссылке:
function addToArray(arr) {
arr.push(4);
console.log('Inside function:', arr); // [1, 2, 3, 4]
}
let numbers = [1, 2, 3];
addToArray(numbers);
console.log('Outside function:', numbers); // [1, 2, 3, 4] (изменилось!)
Но переприсваивание массива не влияет на оригинал:
function reassignArray(arr) {
arr = [10, 20, 30]; // новый массив
}
let original = [1, 2, 3];
reassignArray(original);
console.log(original); // [1, 2, 3] (не изменилось)
5. Как защитить от нежелательных изменений
Создание копии
function safeModify(obj) {
// Поверхностная копия (shallow copy)
const copy = { ...obj };
copy.name = 'Modified';
return copy;
}
const original = { name: 'John', age: 30 };
const modified = safeModify(original);
console.log(original); // { name: 'John', age: 30 }
console.log(modified); // { name: 'Modified', age: 30 }
Для массивов
function safeModifyArray(arr) {
// Создаём копию массива
const copy = [...arr];
copy.push(99);
return copy;
}
const original = [1, 2, 3];
const modified = safeModifyArray(original);
console.log(original); // [1, 2, 3]
console.log(modified); // [1, 2, 3, 99]
Глубокая копия (для вложенных объектов)
function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj));
}
// Или используя structuredClone (экспериментальный)
function modernDeepCopy(obj) {
return structuredClone(obj);
}
const original = {
user: { name: 'John' },
tags: ['js', 'node']
};
const copy = deepCopy(original);
copy.user.name = 'Jane';
copy.tags.push('backend');
console.log(original); // не изменилось
console.log(copy); // изменилось
6. Spread оператор и Object.assign()
function modifyWithSpread(obj) {
return { ...obj, name: 'Modified' };
}
const original = { name: 'John', age: 30 };
const modified = modifyWithSpread(original);
console.log(original); // { name: 'John', age: 30 }
console.log(modified); // { name: 'Modified', age: 30 }
// Object.assign()
function modifyWithAssign(obj) {
const copy = Object.assign({}, obj);
copy.name = 'Modified';
return copy;
}
7. Практические примеры
Ошибка: неожиданное изменение объекта
function updateUser(user) {
user.lastLogin = new Date(); // изменяет оригинальный объект!
return user;
}
const userData = { id: 1, name: 'John' };
const returned = updateUser(userData);
console.log(userData); // { id: 1, name: 'John', lastLogin: Date }
// userData изменился!
Правильно: возвращаем новый объект
function updateUser(user) {
return {
...user,
lastLogin: new Date()
};
}
const userData = { id: 1, name: 'John' };
const updated = updateUser(userData);
console.log(userData); // { id: 1, name: 'John' } (не изменился)
console.log(updated); // { id: 1, name: 'John', lastLogin: Date }
8. Функции с множественными параметрами
function processData(primitives, obj, arr) {
// Примитивы — копия
let num = primitives; // локальная копия
// Объект — ссылка
obj.modified = true; // изменит оригинальный
// Массив — ссылка
arr.push(99); // изменит оригинальный
}
let value = 10;
let object = { name: 'test' };
let array = [1, 2, 3];
processData(value, object, array);
console.log(value); // 10 (не изменился)
console.log(object); // { name: 'test', modified: true } (изменился!)
console.log(array); // [1, 2, 3, 99] (изменился!)
9. Параметры по умолчанию и rest параметры
// Параметры по умолчанию
function greet(name = 'Guest', obj = {}) {
obj.greeted = true; // изменит переданный объект или новый
}
// Rest параметры (собирают все в массив)
function sum(...numbers) { // numbers — новый массив
return numbers.reduce((a, b) => a + b, 0);
}
const nums = [1, 2, 3];
sum(...nums); // распаковка, но передаются примитивы
10. Деструктуризация параметров
function processUser({ name, email, age = 18 }) {
// деструктуризирует параметр-объект
// это поверхностная копия свойств
name = 'Modified'; // не влияет на исходный
}
const user = { name: 'John', email: 'john@example.com' };
processUser(user);
console.log(user.name); // 'John' (не изменился)
Сводная таблица
| Тип | Передача | Изменение влияет | Пример |
|---|---|---|---|
| number | По значению | Нет | 10 |
| string | По значению | Нет | 'hello' |
| boolean | По значению | Нет | true |
| null/undefined | По значению | Нет | null |
| object | По ссылке | Да (свойства) | {} |
| array | По ссылке | Да (элементы) | [] |
| function | По ссылке | Да (вызов) | function(){} |
Лучшие практики
- Помните о ссылках — объекты и массивы изменяют оригиналы
- Используйте spread —
{ ...obj }для копий - Возвращайте новые значения — вместо изменения параметров
- Документируйте побочные эффекты — если функция изменяет параметры
- Избегайте мутаций — функциональное программирование безопаснее
Заключение
В JavaScript примитивы передаются по значению (копируются), а объекты и массивы — по ссылке (указатель на объект). Это может привести к неожиданным изменениям, если не быть осторожным. Используйте spread оператор, Object.assign() или методы копирования, чтобы защитить исходные данные.