Функциональные приёмы

Чистые функции, каррирование и композиция — кирпичики функционального стиля в JS.

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

Чистые функции и иммутабельность

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

// чистая: возвращает новый массив, не трогает вход
function addPure(arr, item) { return [...arr, item]; }

const cart = [1, 2, 3];
const result = addPure(cart, 4);
console.log("новый:", result.join(", "));
console.log("исходный не тронут:", cart.join(", "));

Вывод:

новый: 1, 2, 3, 4
исходный не тронут: 1, 2, 3

Каррирование

Каррирование превращает функцию многих аргументов в цепочку функций по одному аргументу. Это позволяет «зафиксировать» часть аргументов и переиспользовать частично применённую функцию. Универсальный curry:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) return fn.apply(this, args);
    return (...next) => curried.apply(this, args.concat(next));
  };
}

const sum = (a, b, c) => a + b + c;
const cs = curry(sum);
console.log(cs(1)(2)(3));
console.log(cs(1, 2)(3));
console.log(cs(1)(2, 3));
console.log(cs(1, 2, 3));

Вывод:

6
6
6
6

Все четыре способа вызова дают один результат: curry накапливает аргументы, пока их не хватит, и только тогда вызывает исходную функцию.

Композиция функций

Композиция собирает большую функцию из маленьких: выход одной идёт на вход следующей. compose применяет функции справа налево, pipe — слева направо (читается естественнее):

const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);

const double = n => n * 2;
const increment = n => n + 1;
const square = n => n * n;

// double(3)=6 -> +1=7 -> square=49
const f = compose(square, increment, double);
console.log("compose(3):", f(3));

const g = pipe(double, increment, square);
console.log("pipe(3):", g(3));

Вывод:

compose(3): 49
pipe(3): 49

Декларативная обработка данных

Функциональный стиль раскрывается в цепочках методов массива. Вместо ручных циклов — читаемый конвейер filter → map → reduce, где каждый шаг — чистое преобразование:

const orders = [
  { id: 1, total: 120, paid: true },
  { id: 2, total: 80, paid: false },
  { id: 3, total: 200, paid: true },
  { id: 4, total: 50, paid: true }
];

const revenue = orders
  .filter(o => o.paid)
  .map(o => o.total)
  .reduce((sum, t) => sum + t, 0);

console.log("выручка с оплаченных:", revenue);

Вывод:

выручка с оплаченных: 370

Итог

  • Чистые функции без побочных эффектов предсказуемы и тестируемы; не мутируйте вход.
  • Каррирование фиксирует часть аргументов для переиспользования.
  • Композиция (compose/pipe) и цепочки filter/map/reduce делают код декларативным.
Проверьте себя
1. Какая функция считается чистой?
AЛюбая стрелочная функция
BТа, что при одних аргументах даёт один результат и без побочных эффектов
CТа, что не принимает аргументов
DТа, что меняет глобальные переменные
2. Что делает каррирование?
AУскоряет функцию
BПревращает функцию многих аргументов в цепочку функций по одному
CКэширует результат
DДелает функцию асинхронной
3. Чем pipe отличается от compose?
AНичем
Bpipe применяет функции слева направо, compose — справа налево
Cpipe работает только с числами
Dcompose асинхронен
Поддержать проект