Главная Категории Контакты Поиск

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

Основы функционального программирования в JavaScript.

JavaScript·24.12.2019·читать 3 мин 🤓·Автор: Alexey Myzgin

Цель функционального программирования состоит в том, чтобы минимизировать побочные эффекты и разделить функции так, чтобы в случае ошибки ты точно знал, где её найти.

Неизменность (Immutability)

Неизменность заключается не в изменении состояния, а в копировании старого состояния, изменении новой копии и замене старого состояния новым.

Неизменяемые объекты проще создавать, тестировать и использовать. Действительно, неизменяемые объекты всегда потокобезопасны. Они помогают избежать временной связи.

Временная связь - это связь, которая возникает, когда есть два или более члена класса, которые должны быть вызваны в определенном порядке. Использование неизменяемых объектов происходит без побочных эффектов.

Постоянная структура данных - это структура данных, которая всегда сохраняет свою предыдущую версию при её изменении. Такие структуры данных фактически неизменяемы, так как их операции не обновляют (заметно) структуру на месте, а вместо этого, всегда дают новую обновленную структуру.

Подробнее о mutable и immutable структуры данных.

Идемпотентность

Идемпотентность (Idempotent) - означает предсказуемость, т.е. при одном и том же вводном значении, функция всегда должна возвращать один и тот же результат.

Императивный и декларативный

Императивный код - это код, который сообщает компьютеру, что и как делать. Компьютеры хороши с императивными инструкциями. Например: «Пожалуйста, подойди к столу, правой рукой возьми воду, вернись ко мне и дай воды».

for (let i = 0; i < 1000; i++) {
  console.log(i);
}

Декларативный код - это код, который сообщает компьютеру, что делать и что должно происходить; он не говорит машине, как именно это сделать. Люди хороши в декларативных инструкциях. Например: «Пожалуйста, дай мне эту воду».

В этом примере мы не указываем, что начальный индекс 0 и что после каждой итерации его нужно увеличивать.

[1, 2, 3].forEach(i => console.log(i));

Карринг

Карринг (curry) - преобразует функцию с несколькими аргументами в серию функций, каждая из которых принимает один аргумент.

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

function multiply(a) {
  return (b) => {
    return (c) => {
      return a * b * c;
    }
  }
}
console.log(multiply(1)(2)(3)); // 6

Подробнее о карринг.

Частичное применение

Частичное применение (partial application) похоже на карринг - это процесс создания функции с меньшим количеством параметров.

Также, частичное применение - это применение к функции некоторых аргументов и возврат новой функции, в ожидании остальных аргументов. Примененные аргументы хранятся в замыкании (closure) и остаются доступными для любых из возвращенных функций в будущем, которые частично применены.

Подробнее о частичном применении.

Запоминание

Запоминание (memoization) - особый вид кеширования. Он кеширует возвращаемое значение на основе своих параметров. Таким образом, если параметры совпадают, вместо поиска функции и повторного вычисления возвращаемого значения, выполняется поиск значения. Это может сэкономить ценные вычислительные циклы.

Чтобы не загрязнять глобальное пространство имен, используем замыкание:

function memoizedAdd80WithClosure() {
  let cache = {};
  return function(n) {
    if (n in cache) {
      return cache[n]
    } else {
      console.log('симуляция длительного вычисления');
      cache[n] = n + 80;
      return cache[n];
    }
  }
}

const memoizedAndEnclosedFunction = memoizedAdd80WithClosure();

memoizedAndEnclosedFunction(80)
симуляция длительного вычисления
160

memoizedAndEnclosedFunction(80)
160

memoizedAndEnclosedFunction(80)
160

Чистые функции

  • чистые функции всегда должны возвращать один и тот же вывод при одинаковом вводе;
  • чистые функции легко тестировать, легко составлять и избегать ошибок;
  • нет побочных эффектов: чистые функции не могут ничего изменить вне их самих.

Функция с побочным эффектом, изменяет массив вне функции:

const arr = [1, 2, 3];

const mutateArray = (arr) => {
  arr.pop();
}

mutateArray(arr);
console.log(arr) //  [1, 2]

Та же функциональность, но с чистой функцией (без побочных эффектов, так как создает новый массив с concat() и его же возвращает). Это не влияет ни на что, вне его сферы действия.

const arr = [1, 2, 3];

const removeLastItem = (arr) => {
  const newArr = [].concat(arr);
  newArr.pop();
  return newArr;
}

removeLastItem(arr); // [1, 2]
console.log(arr) // [1, 2, 3]

Подробнее о чистых функциях.

Ссылочная прозрачность

Ссылочная прозрачность (referential transparency) - обычно определяется как факт, что выражение в программе может быть заменено его значением (или чем-либо, имеющим то же значение) без изменения результата программы. Это подразумевает, что методы всегда должны возвращать одно и то же значение для данного аргумента, не оказывая никакого другого влияния.

const add = (x, y) => x + y;
add(1, 2); // 3

При повторном вызове метода add с теми же вводными, можно заменить его на значение 3.

Эта концепция важна, так как означает, что когда у нас есть ссылочно-прозрачная функция, она легко поддается кэшированию.

Композиция

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

const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);

Подробнее о композиции.

Website, name & logo
Copyright © 2022. Alex Myzgin