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

Каррирование функций в JavaScript: понятие, примеры и практическое применение

5 min read JavaScript Обновлено 29 Dec 2025
Каррирование функций в JavaScript
Каррирование функций в JavaScript

Женщина работает за MacBook Pro

Каррирование — это функциональная техника, которая делает JavaScript-код более выразительным и модульным. В статье объясняется, что такое каррирование, как получать частично применённые функции, как использовать каррирование для композиции функций, а также варианты применения и ограничения.

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

Каррирование названо в честь математика Хаскела Б. Карри и берёт корни в лямбда-исчислении. Суть: функцию с несколькими параметрами преобразуют в последовательность унарных функций — каждая принимает по одному аргументу и возвращает следующую функцию, пока не будут переданы все параметры.

Определение в одну строку: каррированная функция принимает один аргумент и возвращает либо результат (если параметры закончились), либо следующую функцию.

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

Базовый пример каррирования

Ниже — пример каррированной функции (JavaScript). Код сохранён в оригинальном виде для точности:

function buildSandwich(ingredient1) {  
  return (ingredient2) => {  
    return (ingredient3) => {  
      return `${ingredient1},${ingredient2},${ingredient3}`  
    }  
  }  
}

Функция buildSandwich возвращает вложенные анонимные функции: сначала принимает ingredient1, затем ingredient2, затем ingredient3 и, наконец, возвращает строку-результат. Когда вы вызываете buildSandwich(“Bacon”), вы получаете функцию, которая ждёт следующий аргумент.

console.log(buildSandwich("Bacon"))

Вывод консоли браузера: функция возвращает другую функцию.

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

buildSandwich("Bacon")("Lettuce")("Tomato")

Такая запись показывает, что buildSandwich фактически распадается на три унарные функции. Для более компактного синтаксиса часто используют стрелочные функции:

const buildMeal = ingred1 => ingred2 => ingred3 =>
`${ingred1}, ${ingred2}. ${ingred3}`;

buildMeal("Bacon")("Lettuce")("Tomato")

Стрелочные функции делают код короче и чище, особенно при глубокой вложенности.

Частично применённые функции

Частичное применение — практическая цель каррирования. Вы фиксируете часть аргументов и получаете новую функцию с «заблокированными» значениями.

Простой нелинейный пример (обычная функция):

const multiply = (x, y) => x * y;

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

const curriedMultiply = x => y => x * y;

Фиксируем множитель 10:

const timesTen = curriedMultiply(10);
console.log(timesTen(8)) // 80

Это удобно, когда одна и та же операция применяется с разными данными, но с одним общим параметром.

Ещё пример ближе к DOM и веб-разработке:

const updateElemText = id = content  
  => document.querySelector(`#${id}`).textContent = content

// Lock the element's id into the function:
const updateHeaderText = updateElemText('header')

// Update the header text
updateHeaderText("Hello World!")

Тут updateElemText фиксирует id, возвращая функцию, которая обновляет текст для конкретного элемента.

Композиция функций с каррированием

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

Пример из e‑commerce: три обёртки-функции, которые логируют и передают выполнение дальше:

const addCustomer = fn => (...args) => {  
  console.log("Saving customer info")  
  return fn(...args)  
}

const processOrder = fn => (...args) => {  
  console.log(`processing order #${args[0]}`)  
  return fn(...args);  
}

let completeOrder = (...args) => {  
  console.log(`Order #${[...args].toString()} completed.`);  
}

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

completeOrder = (processOrder(completeOrder));
completeOrder = (addCustomer(completeOrder));
completeOrder("1000")

Скриншот: результат композиции каррированных функций.

Альтернативный «обычный» вариант показан в исходном тексте — выглядит громоздко из‑за вложенных function-выражений.

Автоматическое преобразование в каррированные функции

Если вы используете каррирование часто, удобно иметь утилиту, которая превращает обычную функцию в каррированную. Один из распространённых подходов — рекурсивный curry:

const curry = (fn) => {  
  return curried = (...args) => {  
    if (fn.length !== args.length) {  
      return curried.bind(null, ...args)  
    }  

    return fn(...args);  
  }  
}

Пример применения:

const total = (x, y, z) => x + y + z
const curriedTotal = curry(total)
console.log(curriedTotal(10)(20)(30)) // 60

Замечание: реализация выше ориентирована на фиксированное число параметров fn.length; для функций с опциональными/остаточными параметрами нужно другую стратегию.

Когда каррирование не подходит

Important: каррирование — мощный инструмент, но не универсальный. Обычные сценарии, где каррирование нецелесообразно:

  • Простые одноразовые вызовы, где нет повторного использования частично применённых версий. Каррирование добавит лишнюю сложность.
  • Функции с динамическим количеством аргументов (rest-параметры) — стандартная curry-реализация на fn.length не работает корректно.
  • Когда важна читаемость для команды, незнакомой с функциональным стилем. Иногда явное перечисление аргументов легче воспринимается.
  • Производительность: каррирование создаёт дополнительные замыкания; в «горячих» участках кода это может быть ощутимо.

Альтернативы и дополнения

  • Частичное применение (partial application) как отдельная техника — фиксирует несколько аргументов без полной трансформации в унарные функции.
  • Паттерн фабрик функций: вместо каррирования вернуть функцию-конфигуратор, принимающий объект конфигурации.
  • Использовать библиотеку (lodash/fp, Ramda), чтобы получить проверенные curry/compose/pipe утилиты.
  • Для работы с несколькими аргументами и опциями удобнее применять объекты с именованными полями (options) — это решает проблемы порядка аргументов.

Шпаргалка: быстрые приёмы и шаблоны

  • Создание частично применённой функции: const f2 = curry(f)(a)
  • Комбинация логирующей обёртки: const withLog = fn => (…a) => { console.log(a); return fn(…a)}
  • Составление пайплайна: const pipeline = (…fns) => arg => fns.reduce((v, fn) => fn(v), arg)

Пример pipeline:

const pipeline = (...fns) => x => fns.reduce((v, f) => f(v), x)
const trim = s => s.trim()
const toLower = s => s.toLowerCase()
const exclaim = s => s + '!'

const shout = pipeline(trim, toLower, exclaim)
console.log(shout('  Hello  ')) // "hello!"

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

  • Функция корректно возвращает частично применённую версию при передаче меньшего числа аргументов.
  • Поведение для функций с rest-параметром документировано и ожидаемо.
  • Нет утечек памяти при частом создании частично применённых функций (проверить в профайлере).
  • Логирование/побочные эффекты не теряются при обёртках и композиции.

Модель принятия решения: использовать каррирование или нет

flowchart TD
  A[Нужно ли фиксировать часть аргументов?] -->|Да| B[Есть ли фиксированное число аргументов?]
  A -->|Нет| C[Не использовать каррирование]
  B -->|Да| D[Использовать каррирование или curry-утилиту]
  B -->|Нет| E[Использовать объекты-конфигурации или partial с именованными параметрами]
  D --> F[Проверить производительность]
  F --> G[Ок внедрить]

Роли и чеклист внедрения

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

    • Выяснить, где повторно используется набор аргументов.
    • Написать unit-тесты для частично применённых версий.
    • Использовать существующую curry-утилиту (локальную или из библиотеки).
  • Руководитель команды:

    • Оценить эффект на читабельность и обучаемость команды.
    • Решить стандарты использования (когда применять каррирование).
  • QA:

    • Проверить сценарии с разным числом аргументов.
    • Протестировать побочные эффекты и взаимодействие с DOM (если есть).

Краткая справка (глоссарий)

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

Заключение

Каррирование — удобный инструмент функционального программирования в JavaScript. Оно помогает строить переиспользуемые, легко конфигурируемые функции и упрощает композицию. Но важно учитывать читаемость, характеристики производительности и требования к входным аргументам. Выберите каррирование там, где оно даёт чистую практическую выгоду: конфигурация, создание API‑обёрток, пайплайны обработки данных.

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

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

Трекер чтения и виртуальная полка в Notion
Продуктивность

Трекер чтения и виртуальная полка в Notion

Сберегательный счёт Apple для держателей Apple Card
Финансы

Сберегательный счёт Apple для держателей Apple Card

Карточки в Google Таблицах: Flippity шаг за шагом
Образование

Карточки в Google Таблицах: Flippity шаг за шагом

Избегать платных дорог и шоссе в Google Maps
Навигация

Избегать платных дорог и шоссе в Google Maps

Массовые выплаты PayPal: экономьте на комиссиях
Платежи

Массовые выплаты PayPal: экономьте на комиссиях

Как установить Google Maps на Windows
Руководство

Как установить Google Maps на Windows