Что такое нечистая функция?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое нечистая функция?
Нечистая функция (impure function) - это функция, которая имеет побочные эффекты (side effects) или зависит от внешнего состояния помимо своих аргументов. Противоположность нечистой функции - чистая функция (pure function), которая всегда возвращает одинаковый результат при одинаковых входных данных и не имеет побочных эффектов. Понимание разницы между ними критично для написания надёжного и предсказуемого кода.
Что такое чистая функция?
Сначала разберёмся с чистыми функциями для сравнения. Чистая функция должна:
- Всегда возвращать один и тот же результат при одинаковых входных параметрах
- Не иметь побочных эффектов (не изменять внешнее состояние)
- Не зависеть от внешних переменных
- Не вызывать другие нечистые функции
// ЧИСТАЯ ФУНКЦИЯ
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
console.log(add(2, 3)); // 5
console.log(add(2, 3)); // 5
// Всегда один результат! Нет побочных эффектов.
// ЧИСТАЯ ФУНКЦИЯ - работа со строками
function toUpperCase(str) {
return str.toUpperCase();
}
console.log(toUpperCase('hello')); // 'HELLO'
console.log(toUpperCase('hello')); // 'HELLO'
// Всегда одинаковый результат
Что такое нечистая функция?
Нечистая функция имеет побочные эффекты или зависит от внешнего состояния. Вот основные признаки:
1. Модифицирует внешние переменные
// НЕЧИСТАЯ ФУНКЦИЯ - изменяет глобальное состояние
let counter = 0;
function increment() {
counter++; // Изменяет внешнюю переменную!
return counter;
}
console.log(increment()); // 1
console.log(increment()); // 2
console.log(increment()); // 3
// Результат зависит от количества предыдущих вызовов
// Правильная (чистая) версия:
function incrementPure(count) {
return count + 1; // Не изменяет внешнее состояние
}
2. Зависит от внешнего состояния
// НЕЧИСТАЯ ФУНКЦИЯ - зависит от глобального объекта
const user = { name: 'John', age: 30 };
function getAge() {
return user.age; // Зависит от внешнего объекта
}
getAge(); // 30
user.age = 25; // Меняем внешнее состояние
getAge(); // 25
// Результат изменился без изменения аргументов!
// Чистая версия:
function getAgePure(userData) {
return userData.age; // Явно принимает нужные данные
}
3. Имеет побочные эффекты (I/O операции)
// НЕЧИСТАЯ ФУНКЦИЯ - обращается к DOM
function displayMessage(msg) {
document.getElementById('output').textContent = msg; // Побочный эффект!
return msg;
}
// НЕЧИСТАЯ ФУНКЦИЯ - выполняет HTTP запрос
function fetchUser(id) {
fetch(`/api/users/${id}`) // Побочный эффект!
.then(res => res.json())
.then(data => console.log(data));
}
// НЕЧИСТАЯ ФУНКЦИЯ - логирует в консоль
function calculate(a, b) {
console.log(`Calculating ${a} + ${b}`); // Побочный эффект!
return a + b;
}
4. Работает с текущей датой/временем
// НЕЧИСТАЯ ФУНКЦИЯ - результат зависит от времени выполнения
function getTimestamp() {
return Date.now(); // Результат меняется с каждым вызовом
}
getTimestamp(); // 1704067200000
getTimestamp(); // 1704067201000
// Разные результаты!
// Чистая версия:
function getTimestampPure(currentTime) {
return currentTime; // Передаём время явно
}
5. Изменяет параметры (особенно объекты и массивы)
// НЕЧИСТАЯ ФУНКЦИЯ - мутирует входной параметр
function addUser(users, newUser) {
users.push(newUser); // Изменяет исходный массив!
return users;
}
const userList = [{id: 1}];
const updated = addUser(userList, {id: 2});
console.log(userList); // [{id: 1}, {id: 2}] - ИЗМЕНИЛСЯ!
// Чистая версия:
function addUserPure(users, newUser) {
return [...users, newUser]; // Создаёт новый массив
}
const userList2 = [{id: 1}];
const updated2 = addUserPure(userList2, {id: 2});
console.log(userList2); // [{id: 1}] - НЕ ИЗМЕНИЛСЯ
console.log(updated2); // [{id: 1}, {id: 2}]
Примеры нечистых функций в фронтенде
Пример 1: Нечистая обработка событий
// НЕЧИСТАЯ ФУНКЦИЯ
let formData = {};
function handleInputChange(event) {
formData[event.target.name] = event.target.value; // Мутирует глобальное состояние
saveToLocalStorage(formData); // Побочный эффект!
updateUI(); // Побочный эффект!
}
// ЧИСТАЯ ВЕРСИЯ
function handleInputChangePure(currentData, fieldName, value) {
return { ...currentData, [fieldName]: value }; // Новый объект
}
// Использование в React (правильный способ):
function FormComponent() {
const [formData, setFormData] = useState({});
const handleChange = (e) => {
setFormData(prev => ({
...prev,
[e.target.name]: e.target.value
}));
};
return <input name="email" onChange={handleChange} />;
}
Пример 2: Нечистая фильтрация
// НЕЧИСТАЯ ФУНКЦИЯ
const results = [];
function filterAndStoreUsers(users, minAge) {
for (let user of users) {
if (user.age >= minAge) {
results.push(user); // Мутирует глобальный массив!
}
}
return results;
}
// Вызовы имеют побочные эффекты
const adults = filterAndStoreUsers(users, 18);
const seniors = filterAndStoreUsers(users, 65);
console.log(results); // Содержит ВСЕ результаты из обоих вызовов!
// ЧИСТАЯ ФУНКЦИЯ
function filterUsersPure(users, minAge) {
return users.filter(user => user.age >= minAge);
}
const adults2 = filterUsersPure(users, 18);
const seniors2 = filterUsersPure(users, 65);
// Каждый результат независим
Пример 3: Нечистое преобразование данных
// НЕЧИСТАЯ ФУНКЦИЯ
const cache = {};
function getUserData(id) {
if (!cache[id]) {
const data = fetch(`/api/users/${id}`).then(r => r.json()); // I/O побочный эффект!
cache[id] = data; // Мутирует кэш!
}
return cache[id];
}
// ЧИСТАЯ ФУНКЦИЯ
function getUserDataPure(id, cachedData = {}) {
return cachedData[id] || null;
}
// Кэширование должно быть вне функции
const cache2 = {};
async function getUserDataCached(id) {
if (!cache2[id]) {
const data = await fetch(`/api/users/${id}`).then(r => r.json());
cache2[id] = data; // Побочный эффект отделён
}
return cache2[id];
}
Почему нечистые функции опасны?
1. Непредсказуемость
// Сложно отладить - результат зависит от истории вызовов
function calculate(n) {
return n * globalMultiplier; // Зависит от глобального состояния
}
globalMultiplier = 2;
console.log(calculate(5)); // 10
globalMultiplier = 3;
console.log(calculate(5)); // 15
// Одинаковый вход, разные выходы!
2. Сложное тестирование
// Нечистую функцию сложно тестировать
function nimpureAdd(a, b) {
const result = a + b;
logToServer(result); // Побочный эффект - нужен мок
saveToDatabase(result); // Побочный эффект - нужен мок
return result;
}
// Чистую функцию легко тестировать
function pureAdd(a, b) {
return a + b;
}
// Просто: assert pureAdd(2, 3) === 5
3. Сложная отладка
// Когда значение неправильно, сложно понять почему
let globalState = { count: 0 };
function increment() {
globalState.count++;
}
function decrement() {
globalState.count--;
}
// globalState могла измениться в 100 местах кода!
Как избежать нечистых функций
1. Передавай необходимые данные как параметры
// ПЛОХО
let user = { age: 30 };
function getAdultStatus() {
return user.age >= 18;
}
// ХОРОШО
function getAdultStatus(age) {
return age >= 18;
}
2. Возвращай новые объекты вместо мутирования
// ПЛОХО
function addItem(array, item) {
array.push(item);
return array;
}
// ХОРОШО
function addItem(array, item) {
return [...array, item];
}
3. Отделяй побочные эффекты
// ПЛОХО - смешивает логику и побочные эффекты
function processAndSave(data) {
const processed = data.map(x => x * 2);
saveToDatabase(processed); // Побочный эффект!
return processed;
}
// ХОРОШО - чистая логика отделена
function processData(data) {
return data.map(x => x * 2);
}
const processed = processData(data);
saveToDatabase(processed); // Побочный эффект явный
4. Используй функциональное программирование
// React Hooks - правильный способ управлять побочными эффектами
function Component() {
const [count, setCount] = useState(0);
// Побочный эффект явно отделён
useEffect(() => {
document.title = `Count: ${count}`; // Побочный эффект
}, [count]);
// Чистая функция для обновления состояния
const increment = () => setCount(c => c + 1);
return <button onClick={increment}>Count: {count}</button>;
}
Запомни: нечистые функции - это не "плохо" (иногда они необходимы), но их нужно свести к минимуму и явно отделить от чистой логики. Это делает код более надёжным, тестируемым и поддерживаемым.