Каррирование и частичное применение в JavaScript
Куррирование и частичное применение — это мощные техники функционального программирования, которые могут значительно повысить модульность и повторное использование вашего кода JavaScript. В этой статье мы подробно разберём эти концепции, рассмотрим лучшие практики и приведём практические примеры, которые помогут вам освоить их применение.
Понимание куррирования
Куррирование — это техника, при которой функция преобразуется в последовательность функций, каждая из которых принимает один аргумент. Это позволяет разбить функцию, принимающую несколько аргументов, на ряд унарных (одноаргументных) функций.
Пример куррирования
Рассмотрим простую функцию, которая складывает три числа:
function add(a, b, c) {
return a + b + c;
}Мы можем преобразовать эту функцию в каррированную версию:
Пояснение:
- Функция
addпринимает три аргумента и возвращает их сумму. - Функция
curryAdd— это каррированная версияadd. Она принимает один аргументaи возвращает другую функцию, которая принимаетb. Эта вторая функция возвращает ещё одну функцию, которая принимаетc. Самая внутренняя функция возвращает суммуa,bиc.
Преимущества куррирования
- Повторное использование: Каррированные функции позволяют создавать новые функции, фиксируя некоторые аргументы. Это повышает повторное использование, позволяя легко создавать специализированные версии функции без дублирования кода.
Здесь curriedAdd — это каррированная версия функции add. Она позволяет создавать специализированные функции, такие как add5, фиксируя первый аргумент (a) равным 5. Это способствует повторному использованию кода, поскольку мы можем создавать несколько специализированных функций, не повторяя логику сложения.
- Композиция функций: Куррирование упрощает композицию функций. Композиция функций — это процесс объединения двух или более функций для создания новой функции.
В этом примере мы компонуем функции multiply и addOne с помощью куррирования. Каррируя эти функции, мы можем легко создать новую функцию addOneThenMultiplyBy5, которая сначала прибавляет 1 к входному значению (x), а затем умножает результат на 5. Это показывает, как куррирование облегчает композицию функций, делая создание новых функций путём объединения существующих более простым.
Реализация куррирования в JavaScript
Мы можем создать вспомогательную функцию, чтобы каррировать любую функцию. Вот реализация универсальной функции куррирования:
Пояснение:
Функция куррирования (
curry):- Функция
curryпринимает другую функциюfnв качестве входных данных. - Она возвращает новую функцию под названием
curried. - Эта функция
curriedпринимает любое количество аргументов, используя синтаксис rest-параметра (...args).
- Функция
Каррированная функция (
curried):- Внутри
curriedона проверяет, больше ли или равно количество переданных аргументов (args.length) количеству аргументов, ожидаемых исходной функциейfn(fn.length). - Если передано достаточно аргументов, она вызывает исходную функцию
fnс этими аргументами, используяfn.apply(this, args). - Если аргументов недостаточно, она возвращает новую функцию, которая принимает дополнительные аргументы (
nextArgs) с помощью оператора spread (...nextArgs). - Эта новая функция рекурсивно вызывает
curriedс объединёнными аргументами (args.concat(nextArgs)), гарантируя, что все аргументы будут собраны до вызова исходной функцииfn.
Примечание:
fn.lengthучитывает только параметры без значений по умолчанию и игнорирует rest-параметры.- Внутри
Пример использования:
- Мы определяем функцию
multiply, которая принимает три аргумента и возвращает их произведение. - Мы создаём каррированную версию функции
multiply, передавая её в функциюcurry, которая возвращает новую функциюcurriedMultiply. - Теперь
curriedMultiplyможно вызывать с одним, двумя или тремя аргументами. - Каждый раз, когда мы вызываем
curriedMultiplyс одним или несколькими аргументами, она возвращает новую функцию, пока все аргументы не будут собраны, после чего возвращается результат перемножения аргументов.
- Мы определяем функцию
Изучение частичного применения
Частичное применение — это техника, при которой вы создаёте новую функцию, предварительно подставляя некоторые аргументы исходной функции. Это особенно полезно для создания специализированных функций.
Пример частичного применения
Рассмотрим следующую функцию, которая форматирует сообщение:
function formatMessage(greeting, name) {
return `${greeting}, ${name}!`;
}Мы можем создать частично применённую функцию:
Пояснение:
- Функция
formatMessageпринимает два аргумента,greetingиname, и возвращает отформатированное сообщение. - Функция
partialпринимает функциюfnи некоторые заранее заданные аргументы (...presetArgs). Она возвращает новую функцию, которая принимает оставшиеся аргументы (...laterArgs). - Когда новая функция вызывается, она объединяет
presetArgsиlaterArgsи вызывает исходную функциюfnс этими аргументами. - Используя
partial, мы создаёмgreetHello— функцию, которая всегда использует "Hello" в качестве приветствия. При вызове с именем она возвращает полное сообщение.
Преимущества частичного применения
Упрощение: Создание более простых функций из более сложных
Предположим, у нас есть функция, которая вычисляет итоговую цену товара после применения скидки и налога.
function calculateFinalPrice(price, discount, tax) {
return price - (price * discount) + (price * tax);
}Эта функция требует три аргумента, поэтому её немного неудобно использовать многократно, если ставки скидки и налога часто одинаковы. С частичным применением мы можем упростить это.
В приведённом выше примере applyDiscountAndTax — это частично применённая функция, которая заранее задаёт значения discount и tax. Это облегчает вычисление итоговой цены для разных товаров без необходимости каждый раз указывать ставки скидки и налога.
Повторное использование кода: Использование общей логики функции с разными заранее заданными аргументами
Представим, что у нас есть функция, которая выводит сообщения с разными уровнями важности.
function logMessage(level, message) {
console.log(`[${level}] ${message}`);
}Мы можем создать переиспользуемые функции для разных уровней логирования с помощью частичного применения.
Здесь createLogger — это частично применённая функция, которая задаёт аргумент level. Функции infoLogger и errorLogger теперь можно использовать для вывода сообщений с заранее заданными уровнями логирования, повторно используя общую логику logMessage.
- Улучшенная читаемость: Делает код более читаемым, разбивая сложные функции Рассмотрим функцию, которая форматирует даты в разных стилях.
function formatDate(date, format) {
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
if (format === 'US') {
options.month = 'long';
} else if (format === 'EU') {
options.day = 'numeric';
options.month = 'numeric';
}
return new Date(date).toLocaleDateString(undefined, options);
}Используя частичное применение, мы можем создавать более читаемые функции для разных форматов дат.
createDateFormatter частично применяет аргумент format, в результате чего получаются специальные функции для форматов дат US и EU. Такое разбиение делает код более читаемым и понятным, поскольку каждый форматтер предназначен для конкретного формата.
Эти примеры показывают, как частичное применение в JavaScript может упростить сложные функции, повысить повторное использование кода и улучшить читаемость, делая код более удобным для сопровождения и понимания.
Лучшие практики использования куррирования и частичного применения
Сохраняйте функции чистыми
- Убедитесь, что каррированные и частично применённые функции остаются чистыми, без побочных эффектов. Это облегчает их понимание и тестирование.
Используйте, когда это уместно
- Используйте куррирование и частичное применение, когда они естественно подходят для решаемой задачи.
- Избегайте чрезмерного использования этих техник, поскольку при неосторожном применении они могут сделать код труднее для понимания.
Используйте композицию функций
- Комбинируйте каррированные функции для создания более сложной функциональности. Куррирование хорошо работает с композицией функций, что ведёт к более модульному коду.
Документация и именование
- Тщательно документируйте каррированные и частично применённые функции, чтобы обозначить ожидаемый способ их использования.
- Используйте понятные и описательные имена функций, чтобы передать их назначение.
INFO
Если вы часто используете куррирование и частичное применение, рассмотрите библиотеку Lodash, которая предоставляет удобные вспомогательные функции, такие как _.curry и _.partial.
Сочетание с методами массивов
Куррирование можно эффективно сочетать с методами массивов, такими как map, filter и reduce, для лаконичного и выразительного кода.
Пояснение:
- Функция
curriedMultiplyиспользуется для созданияmultiplyByTwo— функции, которая умножает свой аргумент на 2. - Массив
numbersпреобразуется с помощьюmap, который применяетmultiplyByTwoк каждому элементу, в результате чего получается новый массив удвоенных чисел.
Заключение
Куррирование и частичное применение в JavaScript предлагают мощные техники для упрощения композиции функций, повышения повторного использования кода и улучшения читаемости. Куррирование преобразует функции с несколькими аргументами в последовательность унарных функций, позволяя создавать более гибкий и модульный код. Частичное применение позволяет заранее задавать аргументы функции, облегчая повторное использование кода и упрощение сложных функций. Используя эти концепции функционального программирования, разработчики могут писать более чистый, лаконичный и удобный в сопровождении код на JavaScript.
Practice
What is correct about Currying in JavaScript?