- Частичное применение c замыканием (closure)
- Частичное применение c карринга
- Частичное применение с bind
Частичное применение c замыканием (closure)
Частичное применение - это применение к функции некоторых аргументов и возврат новой функции, в ожидании остальных аргументов. Примененные аргументы хранятся в замыкании (closure) и остаются доступными для любых из возвращенных функций в будущем которые частично примененны. Если ты не знаком с замыканиями, позволь рассказать тебе о них.
В JavaScript мы можем использовать области видимости способом, который позволит нам предоставить состояние функции или объекту, не подвергая это состояние непосредственно внешнему миру. Вот этот процесс и называется замыканием. Например:
function сallCount(fn) {
let count = 0
return (...args) => {
console.log(`This function has been called ${count++} times`)
fn(...args)
}
}
const add = (x, y) => x + y
const addCount = сallCount(add)
addCount(2, 4) // This function has been called 1 times
addCount(7, 5) // This function has been called 2 times
addCount(2, 4) // This function has been called 3 timesФункция сallCount создает переменную count, которая ограничена областью действия тела функции. Мы не можем получить к ней доступ извне этой функции. Однако, анонимная функция, которую мы возвращаем, использует count; так что значение останется доступным для возвращенной функции, пока она существует. Таким образом, новая функция addCount способна выводить состояние count при каждом вызове.
Частичное применение c карринга
Для лучшего понимания частичного применения, советую ознакомится с карри-функциями.
Аргументы, передаваемые функции карри, позволяют нам хранить данные в закрытом виде (closure) для их повторного использования в наших приложениях. Поскольку каждый аргумент, кроме последнего, возвращает новую функцию, мы можем легко создавать повторно-используемые функции, предварительно предоставив некоторые аргументы.
Создаём карри-функцию для извлечения запросов из API, которая использует частичное применение (partial application) для создания функциональности многократного использования. Функция getFromAPI получит baseURL, endpoint и функцию обратного вызова cb, которая выполнится как только мы получим наши данные из запроса.
Используем fetch для получения данных. Наш URL будет комбинацией baseURL и endpoint. Далее превратим наш ответ в JSON. После этого вызываем обратный вызов cb, передав наши данные data, и “ловим” через catch любые ошибки, если такие возникнут.
const getFromAPI = baseURL => endpoint => cb;
fetch(`${baseURL}${endpoint}`)
.then(res => res.json())
.then(data => cb(data))
.catch(err => console.error(err.message));А сейчас давай частично применим baseURL. Одним из общедоступных API, который мы можем использовать, является GitHub API. У нас есть частично-применённая функция getGitHub, в которую мы можем передавать разные endpoint. Создаем функции для запроса пользователей и репозиторий.
const getFromAPI = baseURL => endpoint => cb;
fetch(`${baseURL}${endpoint}`)
.then(res => res.json())
.then(data => cb(data))
.catch(err => {
console.error(err.message);
});
const getGithub = getFromAPI("https://api.github.com");
const getGithubUsers = getGithub("/users"); // https://api.github.com/users
const getGithubRepos = getGithub("/repositories"); // https://api.github.com/repositoriesТеперь у нас есть две новые функции, каждая из которых частично применила один и тот же baseURL, но, так же частично применила и разные endpoint. Последнее, что мы можем передать - это обратный вызов cb, который вызовет fetch. Давай используем функцию getGithubUsers и передадим различные обратные вызовы для каждого из них.
В первом вызове выведем в консоль user.login имена. Основным преимуществом частичного применения является наша способность откладывать оценку функции, в то же время предоставляя некоторые аргументы для хранения и повторного использования этой функции в нашем приложении. Во втором вызове выведем в консоль user.avatar_url.
const getFromAPI = baseURL => endpoint => cb =>
fetch(`${baseURL}${endpoint}`)
.then(res => res.json())
.then(data => cb(data))
.catch(err => {
console.error(err.message);
});
const getGithub = getFromAPI("https://api.github.com");
const getGithubUsers = getGithub("/users"); // https://api.github.com/users
const getGithubRepos = getGithub("/repositories"); // https://api.github.com/repositories
getGithubUsers(data => {
console.log('login', data.map(user => user.login));
});
getGithubUsers(data => {
console.log('avatar_url', data.map(user => user.avatar_url));
});В качестве результата мы получили 2 массива login и avatar_url, которые содержат запрашиваемые нами данные.
Частичное применение с bind
Частичное применение можно осуществить и без карри.
Метод bind функции позволяет нам предоставлять аргументы функции, которые применяются к новой возвращаемой функции, документация здесь.
Первый аргумент bind - это thisArg. Этот аргумент будет связан с this для новой функции. Мы передадим null, потому как не хотим, чтобы что-либо связывалось с this.
Любой аргумент, передаваемый в bind после thisArg, частично применяется как аргумент к функции. Мы можем передать любое количество аргументов, и если передадим меньше, чем ожидает функция, получим частично-применённую функцию.
const multiply = (x, y) => x * y
const multiply2 = multiply.bind(null, 2)
console.log(multiply2(3)) // 6
console.log(multiply2(4)) // 8
console.log(multiply2(5)) // 10Заключение
Частичное применение - это просто естественная комбинация каррированных функций и замыканий. Ранее примененные аргументы сохраняются и доступны для возвращаемых новых функций. Этот процесс может существенно облегчить написание и понимание кода.