Использование этой функциональной техники может сделать ваш код более элегантным.
Функциональное программирование дает нам методы решения проблем в нашем коде. Одно из них, частичное применение, немного сложно понять, но оно позволяет нам писать меньше (звучит интересно, правда?).
Что это?
Частичное приложение начинается с функции. Мы берем эту функцию и создаем новую с одним или несколькими аргументами, уже «установленными» или частично примененными. Это звучит странно, но уменьшит количество параметров, необходимых для наших функций.
Давайте дадим некоторый контекст, когда мы могли бы использовать частичное приложение:
const list = (lastJoin, ...items) => { const commaSeparated = items.slice(0,-1).join(", "); const lastItem = items.pop(); return `${commaSeparated} ${lastJoin} ${lastItem}`; }
Эта маленькая функция принимает одно слово lastJoin
и произвольное число items
. Первоначально list
объявляет commaSeparated
переменную. Эта переменная хранит разделенный запятыми объединенный массив всех элементов, кроме последнего. В следующей строке мы сохраняем последний элемент в items
в переменной lastItem
. Затем функция возвращается с использованием строкового шаблона.
Затем функция возвращает items
в виде строки в формате списка. Например:
list("and", "red", "green", "blue"); // "red, green and blue" list("with", "red", "green", "blue"); // "red, green with blue" list("or", "red", "green", "blue"); // "red, green or blue"
Наша функция list
позволяет нам создавать списки, когда мы хотим. Каждый создаваемый нами тип списка «и», «с», «или» является специализацией общей list
функции. Разве не было бы хорошо, если бы они могли быть собственными функциями ?!
Как использовать частичное приложение
Здесь может помочь частичное применение. Например, чтобы создать функцию listAnd
, мы «устанавливаем» (или частично применяем) аргумент lastJoin
равным «и». Результат этого означает, что мы можем вызвать нашу частично примененную функцию следующим образом:
listAnd("red", "green", "blue"); // "red, green and blue"
Не нужно останавливаться на достигнутом. Мы можем создать множество специализированных функций, частично применив аргумент к нашей функции списка:
listOr("red", "green", "blue"); // "red, green or blue" listWith("red", "green", "blue"); // "red, green with blue"
Для этого нам нужно создать partial
служебную функцию:
const partial = (fn, firstArg) => { return (...lastArgs) => { return fn(firstArg, ...lastArgs); } }
Эта функция принимает функцию fn
в качестве первого параметра и firstArg
в качестве второго. Он возвращает совершенно новую функцию с одним параметром lastArgs
. Это собирает переданные аргументы.
Теперь, чтобы сделать нашу listAnd
функцию, мы вызываем partial
, передавая нашу list
функцию и последнее слово соединения:
const listAnd = partial(list, "and");
Наша listAnd
функция теперь принимает только произвольный список элементов. Эта функция при вызове, в свою очередь, вызовет переданную в list
функцию. Мы видим, что в качестве первого аргумента будет передано «и», а после этого будет собрано lastArgs
.
Мы создали частично примененную функцию. Мы можем использовать эту специализированную функцию снова и снова в нашей программе:
listAnd("red", "green", "blue"); // "red, green and blue"
Дальнейшее развитие
Функция partial
, которую мы создали, призвана проиллюстрировать, как работает частичное приложение. Есть несколько отличных функциональных библиотек JavaScript, в которые встроена эта утилита, например Ramda JS.
Стоит отметить, что даже если вы новичок в частичном применении как концепции, есть все шансы, что вы ее использовали. Если вы когда-либо использовали метод .bind()
для функции, это пример частичного применения. Обычно this
передается в bind для задания контекста. Под капотом он частично применяет this
и возвращает новую функцию.