Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

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

В JavaScript встроен метод sort, который сортирует массив. По умолчанию этот метод сортирует массив весьма замысловатым способом: приводит каждый элемент массива к строковому типу и сравнивает полученные строки на основе порядка следования кодовых точек Unicode. Подобная сортировка подходит для простых ситуаций, например для числовых значений, но перестает работать в случае более сложного сравнения, например тогда, когда каждый элемент массива — объект.

Давайте вообразим ситуацию: на вход в программу приходит список пользователей, который нужно отсортировать по возрасту и вывести на экран.

const users = [
  { name: 'Igor', age: 19 },
  { name: 'Danil', age: 1 },
  { name: 'Vovan', age: 4 },
  { name: 'Matvey', age: 16 },
];

Сортировка по умолчанию не может правильно отсортировать подобный массив. Причем это касается любого вида сортировки, который мог бы нам понадобиться. Мы можем захотеть сортировать по любому параметру (или даже по набору параметров) и в любом порядке. Сортировки нужны часто, и многие из них довольно сложны.

В языках, где функции не являются данными (объектами первого рода), нам пришлось бы для каждого вида сортировки реализовывать свою собственную функцию sort(). Но в JavaScript есть способ лучше. Посмотрим на определение встроенного метода sort():

elements.sort([compareFunction])

Он принимает необязательный параметр compareFunction(), функцию, которая указывает как сортировать данный массив. Общая идея состоит в том, что нам не нужно реализовывать алгоритм сортировки каждый раз для каждой ситуации, ведь он не меняется. Всё, что меняется — элементы, которые сравниваются между собой в процессе сортировки. И метод sort() делегирует взаимодействие с этими элементами вызываемому коду:

const users = [
  { name: 'Igor', age: 19 },
  { name: 'Danil', age: 1 },
  { name: 'Vovan', age: 4 },
  { name: 'Matvey', age: 16 },
];

// Функция принимает на вход сравниваемые элементы массива
const compare = (a, b) => {
  if (a.age === b.age) {
    return 0;
  }

  return a.age > b.age ? 1 : -1;
};

users.sort(compare);

console.log(users);
// => [ { name: 'Danil', age: 1 },
//      { name: 'Vovan', age: 4 },
//      { name: 'Matvey', age: 16 },
//      { name: 'Igor', age: 19 } ]

https://repl.it/@hexlet/js-functions-high-order-functions-sort

Метод sort() выполняет всю работу по непосредственному перемещению элементов в массиве. Но то, какой элемент больше или меньше, — зависит от программиста. Достигается подобная схема за счёт той самой пользовательской функции, которая передаётся при вызове sort(). Эта функция принимает на вход два параметра — sort() отдаёт в неё два элемента, которые она сравнивает в данный момент. В нашем случае элементы — пользователи. Ваша задача — внутри этой функции посчитать, что больше или меньше, и сделать следующее: если элементы равны, то нужно вернуть 0, если первый элемент больше второго, то считается, что они отсортированы неправильно, и возвращается 1, иначе возвращается -1, а sort() производит их сортировку.

Из кода видно, что внутри функции сравнение идёт по свойству age переданных пользователей. Нетрудно догадаться, что эта функция вызывается внутри sort() множество раз (а именно на каждое сравнение). Как только она начнёт возвращать -1 для каждой пары элементов — сортировка завершена.

Метод sort() относится к так называемым функциям высшего порядка (higher order functions). Функции высшего порядка — это функции, которые либо принимают, либо возвращают другие функции, либо делают всё сразу. Такие функции, как правило, реализуют некий обобщённый алгоритм (например, сортировку), а ключевую часть логики делегируют программисту через функцию. Главный плюс от применения таких функций — сокращение дублирования.

У функции, которая передается внутрь метода sort() есть свое название. Подобные функции называют колбеками (callback, обратный вызов). Колбеком становится любая функция, которая вызывается не напрямую программистом, а ее вызывает какая-то функция, в которую мы передаем наш колбек.

В примере выше необязательно создавать константу для функции. Говоря откровенно, их вообще редко записывают в константы. Типичное использование выглядит как прямая передача функции в функцию:

users.sort((a, b) => {
  if (a.age === b.age) {
    return 0;
  }
  return a.age > b.age ? 1 : -1;
});

// То же самое, но используя функцию Math.sign
users.sort((a, b) => Math.sign(a.age - b.age));

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

Осталось рассмотреть то, как происходит вызов внутри. С точки зрения синтаксиса ничего нового не будет.

const say = (fn) => {
  const message = fn();
  console.log(message);
};
// или так:
// const say = (fn) => console.log(fn());

const myCallbackFn = () => 'hi!';
say(myCallbackFn); // => hi!
// или так:
// say(() => 'hi!');

Функция say() делает вызов функции, находящейся внутри параметра fn(). В нашем примере функция возвращает строку, которая тут же выводится на экран.

Функции высшего порядка настолько удобны в большинстве языков, что практически целиком могут заменить использование циклов. Например, канонический код на JS выглядит так:

// Просто демонстрация
// Разбирать его не надо
users
  .filter((user) => user.age >= 16)
  .map((user) => `${user.name} is ${user.age} years old`)
  .join('\n');
// => Igor is 19 years old
//    Matvey is 16 years old

В этом коде присутствует 2 функции высшего порядка (filter() и map()), 2 функции — аргументы и два прохода (это делают функции высшего порядка) по списку пользователей. Код весьма выразителен и лаконичен.

В следующих уроках мы рассмотрим три самые главные функции высшего порядка, которыми можно решать практически любые задачи. Две из них используются в примере выше, это map() и filter(), а третья — reduce(). Они все доступны в стандартной библиотеке JavaScript.

Постепенно, используя эти функции, мы полностью уйдем от использования циклов. JavaScript - язык, в котором логика работы строится на функциях высшего порядка. К ним нужно привыкать и постепенно внедрять в свой арсенал разработчика. Начиная со следующего урока в курсах JavaScript циклы больше не используются. Посмотрите на пример реального кода и обратите внимание на отсутствие циклов. Скоро вы научитесь писать его так же.


Дополнительные материалы

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

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Фронтенд-разработчик
Профессия
Разработка фронтенд-компонентов веб-приложений
1 июня 10 месяцев
Иконка программы Node.js-разработчик
Профессия
Разработка бэкенд-компонентов веб-приложений
1 июня 10 месяцев
Иконка программы Fullstack-разработчик
Профессия
Новый
Разработка фронтенд и бэкенд компонентов веб-приложений
1 июня 16 месяцев

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»