on
Мемоизация и каррирование в JS
Сейчас в JavaScript приносят много техник из функционального программирования. Преимущество в использовании функциональных подходов в JS является лаконичная реализация в пару строк кода и уменшение дублирования. Из этого следует и минусы. Например, в виде читаемости и понимании. Особенно если незнакомы как работает функциональное программирование.
Мемоизация
Мемоизация - это приём, который реализует сохранение результатов выполнения функций для избежания повторных вычислений. Суть достаточна проста - перед каждым вызовом функции происходит проверка вывоза этой функции с такими же аргументами ранее. Если она вызывалась, то возвращается сохранёный результат, иначе происходит вычисление ответа. Далее полученый результат сохраняется и возвращается. Универсальная функция для создания мемоизованной функции достаточно проста:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function memo(fn) {
const cache = {};
const slice = Array.prototype.slice;
return function () {
const args = slice.call(arguments);
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = fn.apply(null, args);
cache[key] = result;
return result;
};
}
Где это можно применить? К примеру, в тех случаях когда нам нужно выполнить сложные и продолжительные вычисления.
Каррирование
Каррирование - это способ создания функций, позволяющий частичное применение аргументов функции. Например, имеется функция add(), вычисляющая сумму двух чисел: x и y. Ниже демонстрируется, как бы мы находили сумму исходя из того, что x имеет значение 5, а y равен 4:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// есть функция сложения двух чисел
function add(x, y) {
return x + y;
}
// так же нам известны значения аргументов
add(5, 4);
// первый шаг – подстановка первого аргумента
function add(5, y) {
return 5 + y;
}
// второй шаг – подстановка второго аргумента
function add(5, 4) {
return 5 + 4;
}
На первом шаге мы не получаем решение, а только другую функцию. Этот шаг и является частичным применением, так как приминили только первый элемент.
Как бы это можно было реализовать в JavaScript с помощью замыкания:
1
2
3
4
5
const add = x => y => x + y;
const fiveAdd = add(5); // typeof fiveAdd(5) === 'function'
fiveAdd(4); // 9
Но такая реализация не является универсальной. Можно реализовать функцию, которая будет выполнять каррирование, где в качестве аргумента будет передаваться любая функция:
1
2
3
4
5
6
7
8
9
10
function curry(fn) {
let slice = Array.prototype.slice;
let args = slice.call(arguments, 1);
return function() {
let fnArgs = args.concat(slice.call(arguments));
return fn.apply(null, fnArgs);
}
}
Где можно применять этот подход? Если в есть функция которая неоднократно вызывается с практически одними и теми же аргументами, то она будет хорошим кандидатом на каррирование. Можно создать новую функцию, используя метод частичного применения параметров к оригинальной функции. Новая функция будет хранить повторяющиеся параметры. Багодаря этому не нужно передавать каждый раз аргументы.