Імутабельність у функціях
Імутабельність (immutability) - це принцип, за яким дані не змінюються після створення, а у функціях він означає заборону мутувати вхідні аргументи: замість зміни на місці функція повертає нове значення. Це один із ключових принципів чистих і передбачуваних функцій: якщо функція змінює переданий масив або об'єкт, вона впливає на зовнішній стан і стає нечистою.
Теорія
TL;DR
- Імутабельність означає, що дані не змінюються після створення.
- У функціях це правило звучить так: не мутуй аргументи, повертай нову структуру.
- Мутація аргументів це побічний ефект: вихідні дані виклику-власника змінюються непомітно.
- Імутабельні інструменти:
map,filter,concat, spread для масивів і{ ...obj }для об'єктів. - Мутабельні операції, яких уникають у чистих функціях:
push,pop,splice,sort,reverse, пряме присвоєння полю. - Рядки в JavaScript імутабельні завжди, змінити їх на місці неможливо.
Швидкий приклад
// mutable: changes the caller's array
function addItemMutable(arr, item) {
arr.push(item);
return arr;
}
// immutable: returns a new array
function addItem(arr, item) {
return [...arr, item];
}
const numbers = [1, 2, 3];
console.log(addItem(numbers, 4)); // [1, 2, 3, 4]
console.log(numbers); // [1, 2, 3], the source is intactМутація проти нової копії
Мутабельний підхід змінює те, що йому передали:
function addItem(arr, item) {
arr.push(item); // changes the original array
return arr;
}
const numbers = [1, 2, 3];
const result = addItem(numbers, 4);
console.log(numbers); // [1, 2, 3, 4], the source data changedІмутабельний варіант створює новий масив і лишає вхідні дані такими, якими вони були:
function addItem(arr, item) {
return [...arr, item]; // creates a new array
}
const numbers = [1, 2, 3];
const result = addItem(numbers, 4);
console.log(numbers); // [1, 2, 3], the source data is preserved
console.log(result); // [1, 2, 3, 4]Функція не змінила вхідні дані, а повернула нову копію, і це і є імутабельність.
Імутабельні та мутабельні операції
| Тип даних | Імутабельний спосіб |
|---|---|
| Масив | map(), filter(), concat(), spread ([...arr]) |
| Об'єкт | { ...obj, key: newValue } |
| Рядок | Завжди імутабельний, у JavaScript рядок змінити не можна |
| Тип даних | Мутабельна операція |
| --- | --- |
| Масив | push(), pop(), splice(), sort(), reverse() |
| Об'єкт | Пряме присвоєння obj.key = value |
Якщо мутабельна операція все ж потрібна, спершу роблять копію: [...arr].sort() замість arr.sort().
Навіщо потрібна імутабельність
- Робить функції чистими та передбачуваними.
- Спрощує налагодження й тестування: вхідні дані після виклику лишаються тими самими.
- Прибирає побічні ефекти, бо ніщо поза функцією не змінюється.
- Дозволяє безпечно розпаралелювати операції, немає гонки за спільні дані.
- Полегшує порівняння станів і відкат, наприклад у React і Redux, де нове посилання означає «дані змінилися».
| Властивість | Імутабельна функція |
|---|---|
| Змінює вхідні дані | Ні |
| Створює нові структури | Так |
| Побічні ефекти | Ні |
| Передбачуваність | Висока |
| Приклади | map, filter, reduce |
Типові помилки
- Використовувати
sort()абоreverse()на переданому масиві: обидва методи змінюють його на місці. - Вважати spread глибоким копіюванням:
{ ...obj }копіює лише верхній рівень, вкладені об'єкти лишаються спільними. - Мутувати аргумент і повертати його ж, вважаючи, що «зовні ніхто не помітить»: виклик-власник тримає те саме посилання.
- Плутати
constз імутабельністю:constзабороняє перепризначення змінної, але не заважає змінювати вміст масиву чи об'єкта. - Створювати копію на кожен крок у гарячому циклі, не вимірявши вартість: в окремих випадках потрібна локальна мутація всередині функції над власною копією даних.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.