Что такое каррирование в JavaScript?

MiddleJavaScript · Frontend·Обновлено 19 августа 2026
Коротко
Каррирование — это преобразование функции с несколькими аргументами в цепочку функций, каждая из которых принимает один аргумент и возвращает следующую функцию, пока не будут переданы все аргументы.

Определение

Каррирование (currying) — техника трансформации функции с несколькими аргументами в последовательность функций, каждая из которых принимает ровно один аргумент и возвращает следующую функцию (либо итоговый результат, когда аргументы закончились). Название происходит от фамилии математика Хаскеля Карри.

Обычная функция:

function sum(a, b, c) {
  return a + b + c;
}
sum(1, 2, 3); // 6

Каррированная версия:

function curriedSum(a) {
  return function (b) {
    return function (c) {
      return a + b + c;
    };
  };
}
curriedSum(1)(2)(3); // 6

Зачем нужно каррирование

  • Позволяет создавать специализированные функции из общих, фиксируя часть аргументов.
  • Упрощает композицию функций в функциональном стиле (pipe/compose), так как каждая функция в цепочке принимает один аргумент.
  • Повышает переиспользование логики: общая функция один раз описана, дальше она конфигурируется под конкретные задачи.
  • Делает код декларативнее при построении обработчиков, валидаторов и фабрик функций.

Каррирование vs частичное применение

Это близкие, но разные вещи. Каррирование всегда превращает функцию от N аргументов в цепочку из N функций, каждая от одного аргумента. Частичное применение (partial application) — это фиксация произвольного количества аргументов заранее, при этом оставшаяся функция может принимать сразу несколько аргументов, не обязательно по одному.

// частичное применение через bind
function multiply(a, b, c) {
  return a * b * c;
}
const double = multiply.bind(null, 2);
double(3, 4); // 24 — сразу два аргумента, это не каррирование

Универсальная функция curry

На собеседовании часто просят написать функцию curry, которая каррирует функцию произвольной арности:

function curry(fn) {
  return function curried(...args) {
    // если аргументов достаточно — вызываем исходную функцию
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    // иначе возвращаем функцию, которая ждёт остальные аргументы
    return function (...rest) {
      return curried.apply(this, args.concat(rest));
    };
  };
}

function sum(a, b, c) {
  return a + b + c;
}

const curriedSum = curry(sum);
curriedSum(1)(2)(3); // 6
curriedSum(1, 2)(3);  // 6
curriedSum(1)(2, 3);  // 6
curriedSum(1, 2, 3);  // 6

Важный нюанс: реализация опирается на fn.length, поэтому не работает корректно с функциями, использующими остаточные параметры или параметры по умолчанию — такую арность нужно указывать вручную.

Практический пример использования

const log = curry(function (level, tag, message) {
  console.log(`[${level}] [${tag}] ${message}`);
});

const logError = log('ERROR');
const logAuthError = logError('AUTH');

logAuthError('Не удалось авторизовать пользователя');
// [ERROR] [AUTH] Не удалось авторизовать пользователя

Такой подход удобен для создания фабрик функций: обработчиков событий, валидаторов и преобразователей данных в pipeline-цепочках.

Что хочет услышать интервьюер

Кандидат даёт точное определение: превращение функции с N аргументами в цепочку функций от одного аргумента

Показывает разницу между каррированием и частичным применением

Может написать вручную каррированную функцию и универсальную curry-обёртку

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

Знает про ограничение с fn.length при работе с rest-параметрами

Пример: Ручное каррирование через вложенные функции

function curriedSum(a) {
  return function (b) {
    return function (c) {
      return a + b + c;
    };
  };
}

curriedSum(1)(2)(3); // 6

Пример: Универсальная функция curry

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

function sum(a, b, c) {
  return a + b + c;
}

const curriedSum = curry(sum);
curriedSum(1)(2)(3); // 6
curriedSum(1, 2)(3);  // 6
curriedSum(1, 2, 3);  // 6

Пример: Практическое применение — фабрика логгеров

const log = curry(function (level, tag, message) {
  console.log(`[${level}] [${tag}] ${message}`);
});

const logError = log('ERROR');
const logAuthError = logError('AUTH');

logAuthError('Не удалось авторизовать пользователя');
// [ERROR] [AUTH] Не удалось авторизовать пользователя

Типичные ошибки

Путают каррирование с частичным применением, считая это одним и тем же

Не могут написать универсальную функцию curry без подглядывания

Забывают, что curry на основе fn.length ломается на функциях с ...rest или значениями по умолчанию

Считают, что каррирование обязательно про производительность, хотя цель — гибкость и композиция

Не приводят практического примера применения, ограничиваясь только теорией

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее