Skip to main content

Side effects

Side effect (побічний ефект) - це будь-яка дія функції, яка змінює стан поза межами самої функції або залежить від зовнішнього стану, а не просто обчислює результат зі своїх аргументів. Саме побічні ефекти роблять код складнішим для тестування та передбачення, тому їх намагаються зібрати в окремому, чітко окресленому шарі програми.

Теорія

TL;DR

  • Побічний ефект - це будь-який вплив функції на зовнішнє оточення або залежність від нього поза поверненим значенням.
  • Класичні приклади: console.log, alert, зміна DOM, fetch, запис у глобальну змінну, робота з файлами.
  • Залежність від зовнішнього стану теж вважається ефектом: Math.random(), Date.now(), читання глобальної змінної.
  • Функція, яка залежить лише від аргументів і нічого навколо не чіпає, називається чистою (pure).
  • Чистий код легше тестувати, передбачати й безпечніше виконувати паралельно.
  • Повністю прибрати ефекти неможливо: без них немає UI, мережі та сховища. Мета - ізолювати та контролювати їх.

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

javascript
let count = 0; // impure: the function changes an outer variable function increment() { count++; } // pure: the result depends only on the arguments function add(a, b) { return a + b; } increment(); // count is now 1, the outside world changed add(2, 3); // always 5, nothing around it changed

Що саме вважають побічним ефектом

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

ПрикладЩо робить
console.log('Hello')Змінює консоль, тобто зовнішнє оточення
alert('Hi!')Взаємодіє з користувачем
document.body.style.color = 'red'Змінює DOM
fetch('/api')Виконує HTTP-запит
Math.random()Залежить від зовнішнього стану (генератор випадкових чисел)
Date.now()Залежить від поточного часу
Зміна глобальної змінноїЗмінює стан програми поза функцією

Нечиста функція проти чистої

Функція з побічним ефектом не лише рахує, а й змінює стан програми:

javascript
let count = 0; function increment() { count++; // changes an outer variable }

Функція без побічних ефектів працює тільки зі своїми аргументами і нічого навколо не торкається:

javascript
function add(a, b) { return a + b; }

Той самий контраст видно на роботі з масивами: мутація вхідних даних - це ефект, а повернення нової копії - ні.

javascript
// impure: mutates the caller's array function addItem(list, item) { list.push(item); return list; } // pure: returns a new array, the input stays untouched function addItemPure(list, item) { return [...list, item]; }

Чому це важливо і як це контролювати

Код без побічних ефектів легше тестувати (не потрібні моки зовнішнього світу), його результат простіше передбачити, і він безпечніший для паралельного виконання та кешування. Але частина ефектів неминуча: без них програма не спілкувалася б із користувачем, сервером чи файлом. Тому на практиці роблять так:

  • обчислення тримають у чистих функціях, а ефекти виносять на край програми (обробники подій, шар запитів до api.example.com, запис у сховище);
  • зовнішні залежності передають аргументом, а не беруть із глобальної області, наприклад function greet(now) { ... } замість виклику Date.now() всередині;
  • замість мутації вхідних об'єктів повертають нові значення;
  • у тестах підмінюють саме цей тонкий шар ефектів, а решту коду перевіряють напряму.
ОзнакаSide effect
Змінює зовнішні даніТак
Залежить від зовнішнього стануТак
Повертає різні результати на однаковому вводіМожливо
Прикладconsole.log, fetch, операції з DOM
Без побічних ефектівЧиста функція

Одним реченням: side effect - це будь-який зовнішній вплив функції (зміна оточення або залежність від нього), не пов'язаний напряму з її поверненим результатом.

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

  • Вважати ефектом лише запис і не помічати читання: Math.random() та Date.now() роблять функцію нечистою, хоча нічого не змінюють.
  • Думати, що чистота - це заборона ефектів. Заборонити їх не можна, можна лише винести їх з ядра логіки.
  • Мутувати переданий об'єкт чи масив і вважати це «просто оптимізацією»: виклик-власник отримує змінені дані й ловить важкі баги.
  • Ховати запит до мережі чи запис у сховище всередині функції з невинною назвою на кшталт getUser(), через що її неможливо викликати в тесті.
  • Плутати побічний ефект із поверненням значення: return - це не ефект, ефект - це все, що функція робить додатково.

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

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

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