JavaScript & TypeScriptМодуль 6: ООП и паттерны
Чистые функции
Иммутабельность и функциональный подход
Цель урока
В этом уроке ты научишься:
- Понимать чистые функции
- Работать с иммутабельными данными
- Избегать побочных эффектов
Что такое чистая функция
Чистая функция:
- При одинаковых входных данных возвращает одинаковый результат
- Не имеет побочных эффектов
// Чистая
function add(a, b) {
return a + b;
}
// Нечистая — зависит от внешней переменной
let multiplier = 2;
function multiply(n) {
return n * multiplier;
}
// Нечистая — побочный эффект
function addToArray(arr, item) {
arr.push(item); // изменяет входной массив
return arr;
}Побочные эффекты
Что считается побочным эффектом:
- Изменение входных параметров
- Изменение глобальных переменных
- Запись в консоль, файл, БД
- HTTP-запросы
- Работа с DOM
// С побочным эффектом
let total = 0;
function addToTotal(n) {
total += n; // изменяет внешнюю переменную
}
// Без побочного эффекта
function calculateTotal(numbers) {
return numbers.reduce((sum, n) => sum + n, 0);
}Иммутабельность
Не изменяй данные — создавай новые.
Массивы
// Плохо — мутация
function addItem(arr, item) {
arr.push(item);
return arr;
}
// Хорошо — новый массив
function addItem(arr, item) {
return [...arr, item];
}
// Удаление
function removeItem(arr, index) {
return [...arr.slice(0, index), ...arr.slice(index + 1)];
}
// Обновление
function updateItem(arr, index, newValue) {
return arr.map((item, i) => i === index ? newValue : item);
}Объекты
// Плохо — мутация
function updateUser(user, name) {
user.name = name;
return user;
}
// Хорошо — новый объект
function updateUser(user, name) {
return { ...user, name };
}
// Вложенные объекты
function updateAddress(user, city) {
return {
...user,
address: {
...user.address,
city
}
};
}Преимущества чистых функций
- Предсказуемость — легко понять, что делает функция
- Тестируемость — не нужно настраивать окружение
- Кэширование — можно мемоизировать результаты
- Параллелизм — нет общих данных
// Легко тестировать
function calculateDiscount(price, percent) {
return price * (1 - percent / 100);
}
// Тест
console.log(calculateDiscount(100, 20) === 80); // trueКомпозиция функций
const add10 = x => x + 10;
const multiply2 = x => x * 2;
const subtract5 = x => x - 5;
// Ручная композиция
const result = subtract5(multiply2(add10(5)));
console.log(result); // 25
// Функция compose
const compose = (...fns) => x =>
fns.reduceRight((acc, fn) => fn(acc), x);
const calculate = compose(subtract5, multiply2, add10);
console.log(calculate(5)); // 25
// Функция pipe (слева направо)
const pipe = (...fns) => x =>
fns.reduce((acc, fn) => fn(acc), x);
const calculate2 = pipe(add10, multiply2, subtract5);
console.log(calculate2(5)); // 25Практика
Задание 1: Чистая функция
Задача: Преобразуй функцию в чистую.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
["a","b"]
Задание 2: Иммутабельное обновление
Задача: Обнови объект без мутации.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
{"name":"Иван","age":26}
{"name":"Иван","age":25}Задание 3: Композиция
Задача: Создай цепочку преобразований.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
49
Проверь себя
- Что такое чистая функция?
- Почему мутация — это плохо?
- Как обновить вложенный объект иммутабельно?