Функции высшего порядка
Функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата, называется функцией высшего порядка.
С функциями в JavaScript вы можете:
- Хранить их в качестве переменных.
- Использовать их в массивах.
- Назначать их в качестве свойств объекта (методов).
- Передавать их в качестве аргументов.
- Возвращать их из других функций.
Как и любой другой фрагмент данных. Вот самое главное, что вам нужно понять.
Функции работают с данными
Строки — это данные
sayHi = (name) => `Hi, ${name}!`;
result = sayHi('User');
console.log(result); // 'Hi, User!'
Числа — это данные
double = (x) => x * 2;
result = double(4);
console.log(result); // 8
Булевой тип данных — это данные
getClearance = (allowed) => allowed ?
'Access granted' :
'Access denied';
result1 = getClearance(true);
result2 = getClearance(false);
console.log(result1); // 'Access granted'
console.log(result2); // 'Access denied'
Объекты — это данные
getFirstName = (obj) => obj.firstName;
result = getFirstName({
firstName: 'Yazeed'
});
console.log(result); // 'Yazeed'
Массивы — это данные
len = (array) => array.length;
result = len([1, 2, 3]);
console.log(result); // 3
Эти 5 типов данных являются объектами первого класса в любом из распространенных языков программирования.
Что делает их “первоклассными”? Вы можете передавать их, хранить их в переменных и массивах, использовать их в качестве исходных данных для вычислений. Вы можете использовать их, как и любой другой фрагмент данных.
Функции тоже могут быть данными
Функции в качестве аргументов
isEven = (num) => num % 2 === 0;
result = [1, 2, 3, 4].filter(isEven);
console.log(result); // [2, 4]
Видите как filter
использует isEven
, чтобы решить, какие числа оставить? Функция isEven
была аргументом другой функции.
Функция isEven
вызывается функцией filter
для каждого числа и использует возвращаемое значение true
или false
, чтобы определить, следует оставить число или выбросить.
Функции возвращающие функцию
add = (x) => (y) => x + y;
add
запрашивает два аргумента, но по отдельности. Эта функция, запрашивающая только x
, которая возвращает функцию, запрашивающую только y
.
Опять же, это возможно только потому, что JavaScript позволяет функциям быть возвращаемым значением — таким же, как строки, числа, булевые значения и т.д.
Если захотите, вы по-прежнему сможете немедленно предоставить x
и y
двойным вызовом функции.
result = add(10)(20);
console.log(result); // 30
Или же x
сейчас, а y
позже.
add10 = add(10);
result = add10(20);
console.log(result); // 30
Давайте вернемся к последнему примеру. add10
— это результат вызова функции add
с одним аргументом. Попробуйте залогировать (logging) это в консоли.
add10
— это функция, которая берет y
и возвращает x + y
. После того, как вы предоставите y
, она поспешит вычислить и вернуть ваш конечный результат.
Возможность повторного использования
Вероятно, самое большое преимущество функций высшего порядка — это возможность повторного использования. Без этих функций, методы массивов JavaScript — map
, filter
, и reduce
не существовали бы!
Ниже представлен список пользователей. Мы собираемся сделать кое-какие вычисления с этой информацией.
users = [{
name: 'Yazeed',
age: 25
}, {
name: 'Sam',
age: 30
}, {
name: 'Bill',
age: 20
}];
Map
Без функций высшего порядка, нам бы потребовались циклы для имитации функциональных возможностей метода map
.
getName = (user) => user.name;
usernames = [];
for (let i = 0; i < users.length; i++) {
const name = getName(users[i]);
usernames.push(name);
}
console.log(usernames);
// ["Yazeed", "Sam", "Bill"]
Но мы может сделать так:
usernames = users.map(getName);
console.log(usernames);
// ["Yazeed", "Sam", "Bill"]
Filter
В мире без функций высшего порядка, нам бы все еще потребовались циклы, чтобы сымитировать функциональные возможности метода filter
.
startsWithB = (string) => string
.toLowerCase()
.startsWith('b');
namesStartingWithB = [];
for (let i = 0; i < users.length; i++) {
if (startsWithB(users[i].name)) {
namesStartingWithB.push(users[i]);
}
}
console.log(namesStartingWithB);
// [{ "name": "Bill", "age": 20 }]
Но мы может сделать так:
namesStartingWithB = users
.filter((user) => startsWithB(user.name));
console.log(namesStartingWithB);
// [{ "name": "Bill", "age": 20 }]
Reduce
Да, это касается и метода reduce
…Нельзя сделать столько крутых вещей без функций высшего порядка! ?
total = 0;
for (let i = 0; i < users.length; i++) {
total += users[i].age;
}
console.log(total);
// 75
Но как насчет этого?
totalAge = users
.reduce((total, user) => user.age + total, 0);
console.log(totalAge);
// 75
Заключение
- Строки, числа, булевые значения, массивы и объекты могут храниться в качестве переменных, массивов, аргументов или методов.
- JavaScript обрабатывает все функции одинаково.
- Map, filter и reduce значительно упрощают преобразование, поиск и суммирование списков.
Перевод статьи Yazeed Bzadough: A quick intro to Higher-Order Functions in JavaScript