← Назад к вопросам

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

2.0 Middle🔥 231 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Каррированием и Частичным Замыканием

Каррирование (Currying) и Частичное замыкание (Partial Application) — это две техники работы с функциями, которые часто путают. Хотя они похожи и связаны, у них есть принципиальные различия.

Каррирование (Currying)

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

// Исходная функция
function add(a, b, c) {
  return a + b + c;
}

add(1, 2, 3); // 6

// Каррированная версия
function curried(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

// Использование
curried(1)(2)(3); // 6

// Можно сохранять промежуточные результаты
const add1 = curried(1);
const add1_2 = add1(2);
const result = add1_2(3); // 6

Ключевые характеристики каррирования:

  • Возвращает функцию, которая ждет ОДИН параметр
  • Каждый уровень вложенности ждет ровно одного аргумента
  • Полностью трансформирует функцию

Стрелочная синтаксис (более читаемо):

const curriedAdd = a => b => c => a + b + c;

curriedAdd(1)(2)(3); // 6
const add1 = curriedAdd(1);
const add1_2 = add1(2);
console.log(add1_2(3)); // 6

Частичное Замыкание (Partial Application)

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

// Исходная функция
function add(a, b, c) {
  return a + b + c;
}

// Функция для создания partial
function partial(fn, ...args) {
  return function(...moreArgs) {
    return fn(...args, ...moreArgs);
  };
}

// Использование
const add5 = partial(add, 5);
console.log(add5(2, 3));        // 10 (5 + 2 + 3)

const add5_10 = partial(add5, 10);
console.log(add5_10(2));        // 17 (5 + 10 + 2)

const add5_10_1 = partial(add5_10, 1);
console.log(add5_10_1());       // 16 (5 + 10 + 1)

Ключевые характеристики partial:

  • Можно зафиксировать ноль или более параметров
  • Возвращаемая функция ждет оставшихся параметров
  • Может принять несколько параметров за раз

Сравнительная таблица

ХарактеристикаКаррированиеPartial
Параметров за разОдинНесколько
Количество уровнейСтрого по числу параметровЛюбое
ГибкостьМенее гибкоБолее гибко
Синтаксис вызоваfn(a)(b)(c)fn(a, b, c)
Интеграция со старым кодомПлохаяХорошая
Фиксирует ВСЕ параметрыОбязательно всеМожет оставить часть

Автоматическое каррирование

// Функция для преобразования любой функции в каррированную
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return function(...moreArgs) {
        return curried(...args, ...moreArgs);
      };
    }
  };
}

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

const curriedMultiply = curry(multiply);

// Все эти варианты работают
console.log(curriedMultiply(2)(3)(4));        // 24
console.log(curriedMultiply(2)(3, 4));        // 24
console.log(curriedMultiply(2, 3, 4));        // 24
console.log(curriedMultiply(2, 3)(4));        // 24

Практические примеры

Каррирование в контексте функционального программирования:

const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

const multiply = x => y => x * y;
const add = x => y => x + y;

const pipeline = compose(
  multiply(2),  // умножить на 2
  add(3)        // прибавить 3
);

console.log(pipeline(5));  // (5 + 3) * 2 = 16

// Каррирование позволяет использовать функции как строительные блоки

Partial в практике:

// Создание специализированных функций
function makeMultiplier(multiplier) {
  return function(num) {
    return num * multiplier;
  };
}

const double = makeMultiplier(2);
const triple = makeMultiplier(3);

console.log(double(5));   // 10
console.log(triple(5));   // 15

// Это по сути partial application

Каррирование в обработке событий:

// Каррированная функция для обработки нажатий
const handleClick = (userId) => (eventType) => (data) => {
  console.log(`User ${userId} triggered ${eventType}:`, data);
};

const userHandler = handleClick(123);
const clickHandler = userHandler('click');

// Позже
button.addEventListener('click', (e) => {
  clickHandler(e.target.value);
});

Partial для конфигурирования:

const fetch = (url, options = {}) => {
  // реальная логика
};

function bind(fn, ...boundArgs) {
  return (...callArgs) => fn(...boundArgs, ...callArgs);
}

// Создать специализированный fetch для конкретного API
const apiCall = bind(fetch, 'https://api.example.com');
const getUserData = bind(apiCall, '/users', { method: 'GET' });

// Использование
apiCall('/products');
getUserData(); // Готов к вызову

Когда использовать каждый подход

Каррирование полезно:

  • В функциональном программировании
  • Для composition функций
  • Когда нужна максимальная модульность
  • Для создания функциональных пайплайнов
// Функциональный стиль с каррированием
const map = fn => arr => arr.map(fn);
const filter = pred => arr => arr.filter(pred);
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);

const nums = [1, 2, 3, 4, 5];
const isEven = n => n % 2 === 0;

const result = pipe(
  filter(isEven),
  map(x => x * 2)
)(nums);

console.log(result); // [4, 8]

Partial полезна:

  • Для создания специализированных функций
  • Когда нужна оптимизация производительности
  • Для интеграции со старым кодом
  • Когда параметры логически связаны

Реальный пример: React

// Каррированная функция для обработчиков
const handleChange = fieldName => event => {
  setState(prev => ({
    ...prev,
    [fieldName]: event.target.value
  }));
};

// Использование
<input onChange={handleChange('name')} />
<input onChange={handleChange('email')} />

// Partial approach
const createChangeHandler = (fieldName) => {
  return (event) => {
    setState(prev => ({
      ...prev,
      [fieldName]: event.target.value
    }));
  };
};

// Эффект почти одинаковый, но partial менее строг

Итог для интервью

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

  • Преобразует функцию f(a, b, c) в f(a)(b)(c)
  • Каждый вызов возвращает функцию, ожидающую ровно один параметр
  • Более теоретический подход, популярен в функциональном программировании

Partial Application:

  • Фиксирует N параметров, оставляя оставшиеся для позднейшего вызова
  • Возвращаемая функция может принять несколько параметров
  • Более практичен и гибок

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

В чем разница между каррированием и частичным замыканием? | PrepBro