Гид по технологиям

Чистые функции в JavaScript

5 min read JavaScript Обновлено 10 Dec 2025
Чистые функции в JavaScript — руководство
Чистые функции в JavaScript — руководство

Большой логотип JavaScript на синем фоне

Чистая функция всегда возвращает одинаковый результат для одинаковых входных данных и не вызывает побочных эффектов. Используйте чистые функции для повышения тестируемости, предсказуемости и возможности кэширования (мемоизации). Там, где требуется взаимодействие с внешним миром, комбинируйте чистые и нечистые функции по границам системы.

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

Чистая функция — это функция, которая:

  • всегда возвращает одно и то же значение при одинаковых входных параметрах;
  • не изменяет внешнего состояния и не выполняет побочных действий (console.log, модификация глобальных переменных, обращение к сети, манипуляции DOM и т.п.).

Краткое определение: чистая функция = детерминизм + отсутствие побочных эффектов.

Характеристики чистой функции

Постоянный результат

Чистая функция возвращает одинаковый результат для одинаковых входных данных, независимо от контекста или числа вызовов.

Пример (чистая):

function multiply(a, b) {
  return a * b;
}

multiply(2, 3); // 6
multiply(2, 3); // 6
multiply(2, 3); // 6

Пример (нечистая):

function multiplyRandomNumber(num) {
  return num * Math.floor(Math.random() * 10);
}

multiplyRandomNumber(5); // непредсказуемо
multiplyRandomNumber(5); // непредсказуемо

Функции с использованием случайности, времени, состояния или внешних источников данных не являются чистыми.

Отсутствие побочных эффектов

Побочный эффект — любое наблюдаемое изменение вне области функции: запись в глобальную переменную, модификация переданного объекта, вывод в консоль, обращение к сети или изменение DOM.

Нечистая версия:

let count = 0;

function increment() {
  count++;
  console.log(count);
}

increment(); // 1
increment(); // 2

Чистая версия — принимаем состояние как аргумент и возвращаем новый результат, не модифицируя внешние переменные:

function increment(count) {
  return count + 1;
}

increment(1); // 2
increment(1); // 2

Дополнительные правила при проектировании чистых функций

  • Не изменяйте переданные аргументы; при необходимости работайте с копиями (например, […arr], {…obj}).
  • Всегда возвращайте значение; функция без возвращаемого значения и без побочных эффектов ничего не делает.
  • Не полагайтесь на внешнее состояние — все зависимости должны приходить через параметры.

Важно: чистота функции определяется её контрактом поведения, а не тем, как она реализована внутри. Функция может быть реализована эффективно и оставаться чистой.

Преимущества чистых функций

Тестируемость

Чистые функции легко тестировать: для заданных входных данных вы всегда знаете ожидаемый результат. Нет необходимости строить сложные подмены окружения или мокать глобальные состояния.

Мемоизация (кеширование результатов)

Поскольку чистые функции детерминированы и не имеют побочных эффектов, их удобно кешировать: для набора аргументов можно хранить уже вычисленный результат и возвращать его повторно. Это полезно для дорогостоящих вычислений.

Простой пример мемоизации для функции с примитивными аргументами:

function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

const slowMultiply = (a, b) => {
  // условно дорогое вычисление
  return a * b;
};

const fastMultiply = memoize(slowMultiply);

fastMultiply(2, 3); // вычисляет и кеширует
fastMultiply(2, 3); // возвращает из кеша

Замечание: мемоизация корректна только для чистых функций. Для функций, зависящих от внешнего состояния, кеш может выдавать устаревшие результаты.

Параллельность и безопасность потоков

Чистые функции не изменяют разделяемое состояние, поэтому их можно безопасно запускать параллельно (в Web Workers, потоках и т.д.). Это уменьшает риск гонок и блокировок.

Предсказуемость и отладка

Из-за детерминизма поведение системы проще воспроизводить и отлаживать. Лог ошибок и воспроизводимые тесты становятся естественной частью процесса.

Когда чистые функции не подходят

Чистые функции — не панацея. Они не решают задачи взаимодействия с внешним миром:

  • Работа с базой данных, сетевые запросы, ввод/вывод файлов — обязательные побочные эффекты.
  • Логирование, чтение системного времени, пользовательский ввод — тоже побочные эффекты.

В таких местах используют нечистые функции, но рекомендуется ограничивать их область и выносить побочные операции к «краям» архитектуры (границы модуля, слой адаптеров).

Практика: как сделать нечистую функцию чистой

  1. Передавайте все внешние зависимости как аргументы (время, генератор случайных чисел, состояние).
  2. Не мутируйте аргументы — возвращайте новые значения.
  3. Выносите побочные эффекты в отдельные функции, которые вызываются один раз в краю приложения.

Пример: чтение и запись состояния (нечистая):

let state = { count: 0 };

function incrementInPlace() {
  state.count += 1; // мутируем глобальное состояние
}

Переработка в чистую логику и нечистый «адаптер»:

// чистая логика
function incrementPure(state) {
  return { ...state, count: state.count + 1 };
}

// нечистый адаптер на краю приложения
function applyIncrement() {
  state = incrementPure(state); // здесь — единственная точка мутации
}

Такой подход упрощает тестирование: incrementPure легко протестировать, а applyIncrement концентрирует побочный эффект в одном месте.

Примеры полезных паттернов и сниппеты

  • Непосредственно чистая обработка массивов:
function addTask(tasks, task) {
  return [...tasks, task]; // не мутируем исходный массив
}
  • Чистая композиция функций:
const compose = (f, g) => x => f(g(x));
  • Пример мемоизации для функций с объектами как аргументами: используйте слабые карты (WeakMap) для сложных структур, но помните о ключах и жизненном цикле данных.

Решающее дерево: стоит ли делать функцию чистой?

flowchart TD
  A[Нужен вывод/побочный эффект?] -->|Да| B[Оставить побочный эффект, но локализовать на краю]
  A -->|Нет| C[Можно ли передать все зависимости как аргументы?]
  C -->|Да| D[Реализовать функцию как чистую]
  C -->|Нет| B
  D --> E[Поддерживает ли функция мутацию аргументов?]
  E -->|Да| F[Копировать аргументы и вернуть новый объект]
  E -->|Нет| G[Можно тестировать и мемоизировать]

Чек-листы по ролям

Разработчик:

  • Передаю ли я все внешние зависимости через параметры?
  • Не мутирую ли я входные данные?
  • Есть ли у функции явный возвращаемый результат?

Ревьюер:

  • Определена ли граница побочных эффектов?
  • Можно ли протестировать функцию изолированно?
  • Подходит ли мемоизация и безопасна ли она здесь?

Тестер:

  • Покрывают ли тесты кейсы с одинаковыми входными данными?
  • Проверены ли случаи с граничными значениями и null/undefined?

Ментальные модели и эвристики

  • Детерминизм: если функция зависит только от аргументов — она, скорее всего, чистая.
  • Границы ответственности: держите побочные эффекты на краю приложения.
  • Минимизация состояния: чем меньше разделяемого состояния, тем проще логика.

Глоссарий в одну строку

  • Побочный эффект — любое изменение или наблюдение вне локальной области функции.
  • Мемоизация — кеширование результатов функции для ускорения повторных вызовов.
  • Детерминизм — свойство возвращать один и тот же результат для одинаковых входных данных.

Когда смешивать чистые и нечистые функции

Архитектурно оптимально: бизнес-логику реализовать через чистые функции, а работу с внешними системами и состоянием — через тонкий слой адаптеров. Это делает код сменяемым, тестируемым и менее подверженным ошибкам.

Итог

Чистые функции повышают надежность, тестируемость и масштабируемость кода. Они особенно полезны в вычислительных задачах, трансформациях данных и там, где важна возможность параллельного выполнения. Однако для взаимодействия с внешним миром побочные эффекты неизбежны — их следует аккуратно изолировать.

Important: начните с малого — делайте чистыми самые критичные и часто тестируемые части приложения. Это даст быстрый выигрыш в качестве кода и удобстве сопровождения.

Критерии приёмки

  • Функция возвращает детерминированный результат для одних и тех же аргументов.
  • Нет модификации внешнего состояния внутри функции.
  • Все внешние зависимости поставляются через параметры или явно документированы.

Сводка

Чистые функции — это простой и мощный инструмент для создания предсказуемого и тестируемого кода. Применяйте их там, где это уместно, и не забывайте локализовать побочные эффекты в отдельные компоненты.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Рабочий стол Windows постоянно обновляется — устранение
Windows

Рабочий стол Windows постоянно обновляется — устранение

Как отключить AdBlock в Windows 10
Руководство

Как отключить AdBlock в Windows 10

Gmail и XMPP: настройка Jabber‑транспорта
Gmail

Gmail и XMPP: настройка Jabber‑транспорта

Запись и публикация геймплея PS4
Гейминг

Запись и публикация геймплея PS4

Поделиться Wi‑Fi через Ethernet в Ubuntu
Ubuntu

Поделиться Wi‑Fi через Ethernet в Ubuntu

Xbox One не видит внешний диск — как исправить
Техподдержка

Xbox One не видит внешний диск — как исправить