Функции высшего порядка

Функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата, называется функцией высшего порядка.

С функциями в 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

Предыдущая статьяОсновы Git за 5 минут
Следующая статьяЗначение Data Science в современном мире