Функциональное программирование в JavaScript
Оглавление
- Функциональная композиция
- Чистая функция
- Неизменяемые данные
- Функции высшего порядка
- Каррирование/Карринг
- Заключение
- Часто задаваемые вопросы
Функциональное программирование — парадигма программирования, в которой особое внимание уделяется использованию функций для преобразования данных. Она направлена на написание кода, который является более декларативным, что облегчает его осмысление и сопровождение с течением времени. JavaScript был принят сообществом функционального программирования как язык, который можно использовать для реализации концепций функционального программирования.
Для начала давайте определим некоторые часто используемые в функциональном программировании термины.
- Функциональная композиция: Это процесс объединения функций для создания более сложных функций. Выход одной функции становится входом для другой, что позволяет создавать сложную логику с помощью простых, многократно используемых функций.
- Чистая функция: Это функция, которая не имеет побочных эффектов и всегда возвращает один и тот же результат при одних и тех же параметрах. Результат чистой функции зависит только от её параметров и не зависит от внешнего состояния.
- Неизменяемые/иммутабельные данные: Это данные, которые не могут быть изменены после создания. Это облегчает рассуждения о состоянии приложения, а также упрощает рассуждения о параллелизме.
- Функции высшего порядка: Функции, которые получают в качестве параметров другие функции или возвращают функции в качестве результатов, называются функциями высшего порядка. Функции высшего порядка являются ключевым понятием в функциональном программировании, поскольку они позволяют абстрагироваться от общих шаблонов и создавать многократно используемые компоненты.
- Каррирование: Каррирование — техника преобразования функции с несколькими аргументами в серию функций с одним аргументом. Это позволяет писать функции, которые можно применять частично, что делает их более гибкими и простыми в использовании.
Теперь, когда у нас есть базовое понимание этих терминов, давайте рассмотрим ключевые концепции функционального программирования на JavaScript.
Функциональная композиция
Композиция функций — процесс объединения функций для создания более сложных функций. Результат одной функции становится параметром для другой функции, что позволяет нам строить сложную логику с помощью простых, многократно используемых функций.
В JavaScript композиция функций может быть достигнута с помощью функции compose
:
const compose =
(...fns) =>
(x) =>
fns.reduceRight((acc, fn) => fn(acc), x);
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const addThenMultiply = compose(multiply, add);
console.log(addThenMultiply(3, 4, 5)); // Результат 35
В этом коде мы используем функцию compose
для создания новой функции, которая сначала складывает два своих аргумента, а затем умножает результат на третий аргумент. Это позволяет нам строить сложную логику с помощью простых, повторно используемых функций, что делает наш код более понятным и поддерживаемым.
Чистая функция
Чистые функции являются основной концепцией функционального программирования и широко используются в JavaScript. Чистая функция — функция, которая при одинаковых параметрах возвращает один и тот же результат и не имеет побочных эффектов. Это делает чистые функции предсказуемыми, тестируемыми и лёгкими для осмысления, что является важным свойством для написания поддерживаемого и масштабируемого кода.
Функция считается чистой, если она удовлетворяет следующим критериям:
- Функция всегда возвращает один и тот же результат при одних и тех же входных данных.
- Функция не имеет побочных эффектов, то есть она не изменяет внешнее состояние, например глобальные переменные или свойства объектов за пределами своей области видимости.
- Функция не зависит от внешнего состояния, то есть для вычисления своего результата она использует только свои входящие данные и локальные переменные.
Рассмотрим следующий пример чистой функции в JavaScript:
const add = (a, b) => a + b;
Эта функция принимает два аргумента, a
и b
, и возвращает их сумму. При одинаковых значениях входных данных она всегда будет возвращать один и тот же результат. Она не изменяет никакого внешнего состояния и не зависит ни от какого внешнего состояния.
Чистые функции обладают рядом преимуществ:
- О них легко рассуждать. Поскольку чистые функции всегда возвращают один и тот же результат при одних и тех же входных данных, легко понять, что делает функция и каким будет её результат.
- Они поддаются тестированию. Поскольку чистые функции детерминированы, мы можем писать тесты, проверяющие их поведение, что облегчает отлов ошибок и повышает качество кода.
- Они поддаются компоновке. Чистые функции можно комбинировать и повторно использовать для создания более сложных функций, что упрощает написание модульного и удобного кода.
- Их можно кэшировать. Результат чистой функции можно кэшировать, что может повысить производительность приложения, особенно если функция требует больших вычислительных затрат.
Неизменяемые данные
В функциональном программировании данные рассматриваются как неизменяемые. Это означает, что после создания данных они не могут быть изменены. В JavaScript этого можно добиться, используя const
для объявления переменных, которые нельзя переназначить, и Object.freeze
, чтобы сделать объекты иммутабельными/неизменяемыми.
const num = 42;
num = 0; // TypeError: Assignment to constant variable.
const obj = { name: "John" };
Object.freeze(obj);
obj.name = "Jane"; // Объект остаётся неизменным.
Использование неизменяемых данных имеет множество преимуществ, в том числе упрощает рассуждения о состоянии приложения и облегчает рассуждения о параллелизме. Когда вы знаете, что данные не могут измениться, вы можете не отслеживать изменения, что делает ваш код более простым и понятным.
Функции высшего порядка
Функции высшего порядка — функции, которые принимают другие функции в качестве входных данных или возвращают функции в качестве выходных данных. Функции высшего порядка — ключевая концепция функционального программирования, поскольку они позволяют абстрагироваться от общих шаблонов и создавать компоненты многократного использования.
Например, рассмотрим следующий код:
const repeat = (fn, n) => {
for (let i = 0; i < n; i++) {
fn();
}
};
const hello = () => console.log("Hello!");
repeat(hello, 3); // Выводит "Hello!" три раза.
В этом коде repeat
является функцией высшего порядка, поскольку принимает функцию в качестве параметра. Функция repeat
может быть использована для повторения любой функции n раз, что делает её многократно используемым компонентом, который может быть использован во всем приложении. Это лишь один из примеров того, как функции высшего порядка можно использовать для абстрагирования от общих шаблонов и сделать наш код более гибким и удобным в обслуживании.
Каррирование/Карринг
Каррирование — техника преобразования функции с несколькими аргументами в серию функций с одним аргументом. Это позволяет писать функции, которые можно применять частично, что делает их более гибкими и простыми в использовании.
В JavaScript каррирование может быть достигнуто с помощью замыканий. Замыкание — функция, которая помнит значения своей внешней области видимости даже после того, как функция была возвращена.
Рассмотрим следующий код:
const curry = (fn) => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(null, args);
} else {
return function (...args2) {
return curried.apply(null, args.concat(args2));
};
}
};
};
const add = (a, b) => a + b;
const curriedAdd = curry(add);
const add5 = curriedAdd(5);
console.log(add5(3)); // Outputs 8
В этом коде мы используем закрытие для создания свёрнутой версии функции add
. Функция curriedAdd
принимает один аргумент и возвращает новую функцию, принимающую второй аргумент. Это позволяет нам частично применять функцию и создавать новые функции с определёнными значениями некоторых аргументов.
Про каррирование есть отличное видео у SOER "Что не так с каррированием на JavaScript?"
Заключение
Функциональное программирование — мощная и элегантная парадигма программирования, обладающая множеством преимуществ, включая упрощение рассуждений о состоянии приложения, упрощение рассуждений о параллелизме и упрощение написания сопровождаемого кода. JavaScript — универсальный и популярный язык программирования, принятый сообществом функционального программирования как язык, который можно использовать для реализации концепций функционального программирования.
В этой статье мы рассмотрели фундаментальные концепции функционального программирования на JavaScript, включая неизменяемые данные, функции высшего порядка, каррирование и композицию функций. Поняв эти концепции, вы сможете писать более чистый, лаконичный и сопровождаемый код на JavaScript.
Часто задаваемые вопросы
Что такое функциональное программирование
Функциональное программирование — парадигма программирования, в которой особое внимание уделяется использованию функций и отказу от использования данных с изменяющимся состоянием и изменяемых данных.
Является ли JavaScript функциональным языком программирования
JavaScript — мультипарадигменный язык программирования, то есть он поддерживает как функциональный, так и объектно-ориентированный стили программирования.
Существуют ли другие функциональные языки программирования
Да. Haskell, Lisp, Scheme и многие другие языки являются функциональными языками программирования.