Функциональное программирование в JavaScript

Источник: «Functional Programming in JavaScript»
В этой статье мы рассмотрим фундаментальные концепции функционального программирования на 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. Чистая функция — функция, которая при одинаковых параметрах возвращает один и тот же результат и не имеет побочных эффектов. Это делает чистые функции предсказуемыми, тестируемыми и лёгкими для осмысления, что является важным свойством для написания поддерживаемого и масштабируемого кода.

Функция считается чистой, если она удовлетворяет следующим критериям:

  1. Функция всегда возвращает один и тот же результат при одних и тех же входных данных.
  2. Функция не имеет побочных эффектов, то есть она не изменяет внешнее состояние, например глобальные переменные или свойства объектов за пределами своей области видимости.
  3. Функция не зависит от внешнего состояния, то есть для вычисления своего результата она использует только свои входящие данные и локальные переменные.

Рассмотрим следующий пример чистой функции в JavaScript:

const add = (a, b) => a + b;

Эта функция принимает два аргумента, a и b, и возвращает их сумму. При одинаковых значениях входных данных она всегда будет возвращать один и тот же результат. Она не изменяет никакого внешнего состояния и не зависит ни от какого внешнего состояния.

Чистые функции обладают рядом преимуществ:

  1. О них легко рассуждать. Поскольку чистые функции всегда возвращают один и тот же результат при одних и тех же входных данных, легко понять, что делает функция и каким будет её результат.
  2. Они поддаются тестированию. Поскольку чистые функции детерминированы, мы можем писать тесты, проверяющие их поведение, что облегчает отлов ошибок и повышает качество кода.
  3. Они поддаются компоновке. Чистые функции можно комбинировать и повторно использовать для создания более сложных функций, что упрощает написание модульного и удобного кода.
  4. Их можно кэшировать. Результат чистой функции можно кэшировать, что может повысить производительность приложения, особенно если функция требует больших вычислительных затрат.

Неизменяемые данные

В функциональном программировании данные рассматриваются как неизменяемые. Это означает, что после создания данных они не могут быть изменены. В 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 и многие другие языки являются функциональными языками программирования.

Дополнительные материалы

Предыдущая Статья

Модернизация конфигурации Symfony

Следующая Статья

Нетерпеливая загрузка может быть вредна