Immutable та mutable дані
Mutable (змінювані) дані можна змінити після створення, а immutable (незмінні) дані змінити не можна, замість цього створюється нове значення. У JavaScript примітиви незмінні, а об'єкти, масиви й функції змінювані.
Теорія
TL;DR
- Mutable, об'єкт можна редагувати після створення:
object,array,function,Map,Set. - Immutable, значення редагувати не можна, лише створити нове: усі примітиви.
constфіксує посилання, а не вміст об'єкта, томуconst user = {}усе одно дозволяєuser.name = '...'.- «Зміна» рядка чи числа завжди створює нове значення, старе лишається недоторканим.
Object.freeze()робить об'єкт незмінним на верхньому рівні, для вкладених потрібен deep freeze.
Швидкий приклад
// mutable
const user = { name: 'Tim' };
user.name = 'Alex';
console.log(user); // { name: 'Alex' }
// immutable
let a = 'Hello';
a[0] = 'J';
console.log(a); // "Hello", нічого не змінилосяВизначення
| Термін | Переклад | Суть |
|---|---|---|
| Mutable | змінюваний | об'єкт, який можна змінювати після створення |
| Immutable | незмінний | дані, які змінити не можна, можна лише створити нові |
Змінювані (mutable) дані
Об'єкти та масиви в JavaScript, це змінювані структури.
const user = { name: 'Tim' };
user.name = 'Alex'; // змінюємо об'єкт
console.log(user); // { name: 'Alex' }Хоча змінну user оголошено через const, її вміст (об'єкт) можна змінити, адже const забороняє міняти саме посилання, а не дані, на які воно вказує.
Те саме з масивом:
const numbers = [1, 2, 3];
numbers.push(4);
console.log(numbers); // [1, 2, 3, 4]Масиви mutable, бо ми можемо додавати, видаляти й змінювати елементи.
Незмінні (immutable) дані
Примітиви в JavaScript незмінні. До них належать: string, number, boolean, null, undefined, symbol, bigint.
let a = 'Hello';
a[0] = 'J'; // спроба змінити рядок
console.log(a); // "Hello", не змінилосяРядки immutable: під час «зміни» створюється новий рядок.
Те саме з числами:
let x = 5;
let y = x;
y = y + 1;
console.log(x); // 5
console.log(y); // 6Числа теж immutable, їх не можна «поміняти», можна лише створити нове значення.
Як це виглядає в пам'яті:
| Тип даних | Змінюваність | Де зберігається | Поведінка |
|---|---|---|---|
Примітив (string, number, boolean та інші) | Immutable | Stack | під час «зміни» створюється нове значення |
| Об'єкт, масив, функція | Mutable | Heap | змінюється за посиланням |
let name = 'Ann';
name = name + 'a';STACK:
+------------------+
| name = "Ann" | <- старий рядок
| name = "Anna" | <- новий рядок (створений заново)
+------------------+Рядок "Ann" не змінюється, створюється новий рядок "Anna".
Чому це важливо
Перше, поведінка під час копіювання:
const user1 = { name: 'Tim' };
const user2 = user1;
user2.name = 'Alex';
console.log(user1.name); // "Alex", змінилися обидва (mutable)Друге, реактивні фреймворки (React, Redux, Vue та інші). Незмінність там ключовий принцип:
- дозволяє відстежувати зміни стану через порівняння посилань;
- полегшує відкат змін (undo/redo);
- робить код передбачуванішим.
Приклад підходу React:
// погано (мутуємо стан напряму)
state.user.age++;
// добре (незмінний апдейт)
setState({ ...state, user: { ...state.user, age: state.user.age + 1 } });Як зробити об'єкт незмінним вручну
const user = Object.freeze({
name: 'Tim',
age: 25
});
user.age = 30; // не спрацює
console.log(user.age); // 25Object.freeze() робить об'єкт immutable на верхньому рівні. Для вкладених властивостей потрібно «заморожувати глибоко»:
function deepFreeze(obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key]);
}
});
return Object.freeze(obj);
}
const data = deepFreeze({
user: { name: 'Tim', skills: ['JS'] }
});
data.user.name = 'Alex'; // не змінитьсяПідсумкова таблиця:
| Категорія | Приклади | Змінюваність | Поведінка |
|---|---|---|---|
| Immutable (незмінні) | string, number, boolean, null, undefined, symbol, bigint | не можна змінити | створюється нова копія |
| Mutable (змінювані) | object, array, function, Map, Set | можна змінити | змінюються «на місці» |
Коротко:
- Immutable, змінити не можна, тільки створити нове значення.
- Mutable, можна змінити «на місці».
- Примітиви immutable, об'єкти та масиви mutable.
- Незмінність робить код надійнішим, особливо в React і Redux.
Типові помилки
- Плутати
constз незмінністю.constпро перепризначення змінної, а не про вміст об'єкта. - Думати, що
Object.freeze()захищає всю структуру. Він поверхневий, вкладені об'єкти лишаються змінюваними. - Не помічати, що в non-strict режимі запис у заморожений об'єкт тихо ігнорується, а в
'use strict'кидаєTypeError. - Мутувати стан у React (
state.items.push(x)) і дивуватися, чому компонент не перемалювався: порівняння за посиланням не бачить різниці. - Вважати, що методи масиву незмінні.
push,splice,sort,reverseмутують оригінал; незмінні аналоги, цеconcat,slice,toSorted,toReversed.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.