← Назад к вопросам

Что такое нечистая функция?

2.3 Middle🔥 211 комментариев
#JavaScript Core#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое нечистая функция?

Нечистая функция (impure function) - это функция, которая имеет побочные эффекты (side effects) или зависит от внешнего состояния помимо своих аргументов. Противоположность нечистой функции - чистая функция (pure function), которая всегда возвращает одинаковый результат при одинаковых входных данных и не имеет побочных эффектов. Понимание разницы между ними критично для написания надёжного и предсказуемого кода.

Что такое чистая функция?

Сначала разберёмся с чистыми функциями для сравнения. Чистая функция должна:

  1. Всегда возвращать один и тот же результат при одинаковых входных параметрах
  2. Не иметь побочных эффектов (не изменять внешнее состояние)
  3. Не зависеть от внешних переменных
  4. Не вызывать другие нечистые функции
// ЧИСТАЯ ФУНКЦИЯ
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>;
}

Запомни: нечистые функции - это не "плохо" (иногда они необходимы), но их нужно свести к минимуму и явно отделить от чистой логики. Это делает код более надёжным, тестируемым и поддерживаемым.