Что такое функция высшего порядка?
Оригинальная статья: Nick Scialli — What is a Higher-Order Function?
Одним из терминов, которые вы можете услышать в мире JavaScript, является «функция высшего порядка». В этой статье мы рассмотрим, что такое функция высшего порядка, на базе нескольких примеров.
Определение
По определению, функция высшего порядка — это функция, которая либо принимает функцию в качестве аргумента, либо возвращает функцию.
Если вы не знакомы с обработкой функций как first class objects, вы удивитесь, но это возможно. А так же, чрезвычайно полезно!
Несколько простых примеров
Давайте рассмотрим пару простых примеров: один для функции, которая принимает функцию в качестве аргумента, и другой, который возвращает функцию.
Принимаем функцию в качестве аргумента
Давайте создадим относительно бесполезную функцию под названием evaluatesToFive, которая принимает два аргумента: первый аргумент будет числом, а второй аргумент будет функцией. Внутри нашей функции evaluatesToFive мы проверим, возвращает ли функция число пять.
function evaluatesToFive(num, fn) { return fn(num) === 5; }
Мы можем это проверить следующим образом:
function divideByTwo(num) { return num / 2; } evaluatesToFive(10, divideByTwo); // true evaluatesToFive(20, divideByTwo); // false
Немного бесполезно, но это круто, что мы можем это сделать!
Возвращаем функцию
В нашем следующем примере мы собираемся создать функцию, которая возвращает функцию. Наша функция будет называться multiplyBy. Она примет число в качестве аргумента и вернет новую функцию, которая умножит свой входной аргумент на это число.
function multiplyBy(num1) { return function(num2) { return num1 * num2; }; }
Теперь мы увидим как это можно использовать, создав пару функций умножения:
const multiplyByThree = multiplyBy(3); const multiplyByFive = multiplyBy(5); multipyByThree(10); // 30 multiplyByFive(10); // 50
Опять же, не супер полезно в его нынешнем виде, но довольно круто, несмотря ни на что.
Более сложный и потенциально полезный пример
Более полезным примером функций высшего порядка будет создание объекта валидатора. Основная идея — это будет функция, которая принимает объект в качестве аргумента, а затем любое количество функций, которые должны вернуть значение true, чтобы объект считался действительным.
В этом примере мы будем обрабатывать объект newUser и пытаться определить, следует ли нам разрешить ему регистрироваться в нашем приложении. Пользователь должен соответствовать следующим критериям:
- Пользователю должно быть не менее 18 лет
- Пароль должен содержать не менее 8 символов
- Пользователь должен согласиться с Условиями обслуживания (Terms of Service)
Идеальный объект newUser будет выглядеть примерно так:
const newUser = { age: 24, password: 'some long password', agreeToTerms: true, };
Основываясь на этих знаниях, мы можем создать некоторые тестовые функции, которые возвращают значение true, когда наши желаемые условия выполняются, и false в противном случае.
function oldEnough(user) { return user.age >= 18; } function passwordLongEnough(user) { return user.password.length >= 8; } function agreeToTerms(user) { return user.agreeToTerms === true; }
Теперь мы можем создать функцию, которая принимает любое количество аргументов. Первым аргументом будет объект, который мы пытаемся проверить, а остальные аргументы будут функциями тестирования, которые будут использоваться для тестирования нашего объекта.
function validate(obj, ...tests) { for (let i = 0; i < tests.length; i++) { if (tests[i](obj) === false) { return false; } } return true; }
Рассмотрим что именно здесь происходит:
- Мы указываем, что нашим первым аргументом функции является объект (obj). Затем мы используем оператор rest (… tests), чтобы сказать, что любые дополнительные аргументы будут в массиве tests.
- Мы используем цикл for, чтобы перебрать наш массив tests, который является массивом функций (это часть функции высшего порядка!).
- Мы передаем obj каждому элементу в массиве tests. Если эта функция возвращает false, мы знаем, что obj недействителен и немедленно возвращаем false.
- Если мы пройдем весь массив tests без возврата false, наш объект будет действительным, и мы вернем true.
Пример использования
Теперь мы используем нашу функцию проверки, проверяя пару потенциальных новых пользовательских объектов:
const newUser1 = { age: 40, password: 'tncy4ty49r2mrx', agreeToTerms: true, }; validate(newUser1, oldEnough, passwordLongEnough, agreeToTerms); // true const newUser2 = { age: 40, password: 'short', agreeToTerms: true, }; validate(newUser2, oldEnough, passwordLongEnough, agreeToTerms); // false
Объект newUser1 правильно считается действительным, а объект newUser2 определяется как недействительный, поскольку его пароль слишком короткий.
Потенциальное улучшение: функция создания валидатора
Если мы применяем нашу функцию validate для нескольких пользователей, вероятно, лучше не повторять одни и те же тесты снова и снова. Вместо этого у нас может быть функция createValidator, которая возвращает валидатор объекта. В этом случае мы создадим userValidator, который применяет те же тестовые функции к любому пользователю, которого мы пытаемся проверить.
function createValidator(...tests) { return function(obj) { for (let i = 0; i < tests.length; i++) { if (tests[i](obj) === false) { return false; } } return true; }; }
Давайте посмотрим, как это дает нам более согласованный интерфейс, когда мы снова проверяем наши объекты newUser1 и newUser2:
const userValidator = createValidator( oldEnough, passwordLongEnough, agreeToTerms ); userValidator(newUser1); // true userValidator(newUser2); // false
Потрясающие! Теперь вы знаете как используя функцию более высокого порядка createValidator, использовать разные критерии проверки для различных объектов.
Не знала про функцию first class objects, действительно очень полезная функция, изучу более детально.
Спасибо за заметку.
Мой вариант:
const createValidator = (fn, …tests) => obj => {
for (const test of tests) {
if (test(obj) === false) return;
}
fn(obj)
}
const userValidator = createValidator(
greet = obj => console.log(`hi ${obj?.name}`),
oldEnough,
passwordLongEnough,
agreeToTerms
)