Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Immutable та mutable дані». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Mutable, це дані, які можна змінити після створення, а immutable, це дані, які змінити не можна: замість зміни створюється нове значення.** У JavaScript усі примітиви (`string`, `number`, `boolean`, `null`, `undefined`, `symbol`, `bigint`) незмінні, а об'єкти, масиви, функції, `Map` і `Set` змінювані. `const` тут не допомагає: він забороняє перепризначити посилання, але не забороняє редагувати об'єкт, на який воно вказує. Щоб заморозити об'єкт, потрібен `Object.freeze()`, а для вкладених структур, рекурсивна глибока заморозка. ```javascript let a = 'Hello'; a[0] = 'J'; console.log(a); // "Hello", рядок незмінний const user = { name: 'Tim' }; user.name = 'Alex'; console.log(user); // { name: 'Alex' }, об'єкт змінюваний ``` **Ключове:** примітиви immutable, об'єкти mutable, а незмінність робить стан передбачуваним, тому на ній тримаються React і Redux.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Mutable (змінювані) дані можна змінити після створення, а immutable (незмінні) дані змінити не можна, замість цього створюється нове значення.** У JavaScript примітиви незмінні, а об'єкти, масиви й функції змінювані. ## Теорія ### TL;DR - **Mutable**, об'єкт можна редагувати після створення: `object`, `array`, `function`, `Map`, `Set`. - **Immutable**, значення редагувати не можна, лише створити нове: усі примітиви. - `const` фіксує посилання, а не вміст об'єкта, тому `const user = {}` усе одно дозволяє `user.name = '...'`. - «Зміна» рядка чи числа завжди створює нове значення, старе лишається недоторканим. - `Object.freeze()` робить об'єкт незмінним на верхньому рівні, для вкладених потрібен deep freeze. ### Швидкий приклад ```javascript // 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, це змінювані структури. ```javascript const user = { name: 'Tim' }; user.name = 'Alex'; // змінюємо об'єкт console.log(user); // { name: 'Alex' } ``` Хоча змінну `user` оголошено через `const`, **її вміст (об'єкт)** можна змінити, адже `const` забороняє міняти саме **посилання**, а не дані, на які воно вказує. Те саме з масивом: ```javascript 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`. ```javascript let a = 'Hello'; a[0] = 'J'; // спроба змінити рядок console.log(a); // "Hello", не змінилося ``` Рядки **immutable**: під час «зміни» створюється новий рядок. Те саме з числами: ```javascript 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 | змінюється за посиланням | ```javascript let name = 'Ann'; name = name + 'a'; ``` ```text STACK: +------------------+ | name = "Ann" | <- старий рядок | name = "Anna" | <- новий рядок (створений заново) +------------------+ ``` Рядок `"Ann"` не змінюється, створюється новий рядок `"Anna"`. ### Чому це важливо Перше, поведінка під час копіювання: ```javascript const user1 = { name: 'Tim' }; const user2 = user1; user2.name = 'Alex'; console.log(user1.name); // "Alex", змінилися обидва (mutable) ``` Друге, реактивні фреймворки (React, Redux, Vue та інші). Незмінність там ключовий принцип: - дозволяє **відстежувати зміни стану** через порівняння посилань; - полегшує **відкат змін** (undo/redo); - робить код **передбачуванішим**. Приклад підходу React: ```javascript // погано (мутуємо стан напряму) state.user.age++; // добре (незмінний апдейт) setState({ ...state, user: { ...state.user, age: state.user.age + 1 } }); ``` ### Як зробити об'єкт незмінним вручну ```javascript const user = Object.freeze({ name: 'Tim', age: 25 }); user.age = 30; // не спрацює console.log(user.age); // 25 ``` `Object.freeze()` робить об'єкт **immutable** на верхньому рівні. Для вкладених властивостей потрібно «заморожувати глибоко»: ```javascript 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`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.