Каррирование
Currying — превращение функции от n аргументов в цепочку n функций, каждая от одного аргумента. По формуле автора: «каррирование — это рекурсивное частичное применение».
Что это / Зачем
- Из
f(a, b, c)делаемf(a)(b)(c) - Можно закрепить часть аргументов и передать остальные позже
- Универсальная абстракция: подойдёт для математики, файлов, рендеринга, фильтрации сигналов
- Связано с замыканиями и Partial Application
Варианты вызова каррированной функции
const f = curry(sum4);
f(1, 2, 3, 4); // все сразу
f(1, 2, 3)(4); // частично + остаток
f(1)(2)(3)(4); // по одному
f(1, 2)(3, 4); // комбинации
Чистая реализация
const curry = (fn) => (...args) =>
args.length >= fn.length
? fn(...args)
: curry(fn.bind(null, ...args));
fn.length— арность функции (число объявленных аргументов)- Если аргументов достаточно — вызываем
fn - Иначе —
bindзакрепляет часть, оборачиваем вcurry
Ключевые правила
fn.lengthне учитывает rest-параметры → REST ломает каррирование- Каждые `` — отдельный вызов, отдельный шаг рекурсии
- Возвращается всегда новая функция, пока аргументов мало
- Каррирование = рекурсивный
partial
Подводные камни
- Дефолтные значения уменьшают
length→ могут сломать арность function(a, b, ...rest)→length === 2, rest игнорируется- Плохая реализация (с глубокой вложенностью IIFE) — нечитаема и не отлаживается
- Стрелочные функции каррируются как и обычные, но без своего
this
🎓 Источник: Частичное применение и каррирование в JavaScript
- 📅 2018-10-08 · YouTube · ID:
ND8KQ5xjk7o - Тезисы:
- «Каррирование — это рекурсивное частичное применение»
- Чистая реализация в 3 строки vs плохая
curryBadс IIFE — урок о читаемости - Универсальная абстракция: подходит для любых функций
fn.lengthопределяет арность, REST её ломает- Оптимизированный
curryпропускает шаг рекурсии при первом вызове
- Цитата: «Запутанно написанный код вы даже никак не отдебажите. Короче, кто сможет исправить вот в этом коде ошибку, считайте, сдали экзамен.»
- Код:
// Оптимизированный curry с частичным применением сразу const curry = (fn, ...args1) => args1.length >= fn.length ? fn(...args1) : (...args2) => curry(fn, ...args1, ...args2); const sum4 = (a, b, c, d) => a + b + c + d; const f = curry(sum4); f(1)(2)(3)(4); // 10 f(1, 2)(3, 4); // 10
🎓 Источник: Вложенные функции в JavaScript, V8 оптимизация
- 📅 2025-02-16 · YouTube · ID:
O62ZgsmB80M - Тезисы:
- Паттерн
(a) => (b) => ...(две стрелочные) — специально оптимизируется V8 - V8 кэширует шаблон возвращаемой функции, не пересоздаёт её каждый раз
- На 3-4 функциях оптимизация становится сложнее, но всё ещё работает
- Замыкание как контекст — альтернатива классу, V8 это «понимает»
- Не бояться таких конструкций — кажущийся оверхед не реален
- Паттерн
- Цитата: «Именно этот паттерн, именно вот такая последовательность из двух функций, особым образом оптимизируют, предполагая, что первая функция специально вызывается ради того, чтобы в замыкании сохранить какие-то аргументы.»