Menu
Русский
Попробовать в Playground

Функции высшего порядка в JavaScript: map, filter, reduce

Разбираемся с функциями высшего порядка в JavaScript: как передавать функции в аргументы, возвращать их из других функций и использовать map, filter и reduce на практике.

Функции — это значения

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

index.js
Output
Click Run to see the output here.

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

Функция как аргумент в JavaScript

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

index.js
Output
Click Run to see the output here.

forEach — это функция высшего порядка: ты передаёшь ей свою функцию, а она вызывает её для каждого элемента. setTimeout — тоже высшего порядка: принимает функцию и вызывает её после задержки. Ты описываешь, что нужно сделать, а они разбираются, когда и сколько раз.

Свою функцию высшего порядка пишут по тому же принципу. Вот крошечный пример — колбэк вызывается только если условие истинно:

index.js
Output
Click Run to see the output here.

action — это параметр, в котором просто лежит функция. Вызов action() запускает то, что в него передали.

Три метода, которыми вы реально будете пользоваться: map, filter, reduce

У массивов в JavaScript есть методы высшего порядка, которые заменяют большинство циклов for, которые вы бы иначе писали руками. Освойте эту тройку — и куча повседневного кода станет короче и понятнее.

map — преобразуем каждый элемент

index.js
Output
Click Run to see the output here.

map вызывает переданную функцию для каждого элемента и собирает возвращённые значения в новый массив. Длина остаётся той же, а содержимое — преобразованное. Исходный массив при этом не меняется.

filter — оставляем только подходящие элементы

index.js
Output
Click Run to see the output here.

filter оставляет только те элементы, для которых колбэк вернул истинное значение, а остальные отбрасывает. На выходе получаем новый массив — возможно, короче исходного.

reduce — сворачиваем массив в одно значение

index.js
Output
Click Run to see the output here.

reduce — самый универсальный из трёх. В колбэк приходят аккумулятор и текущий элемент, а то, что ты вернёшь, станет аккумулятором на следующем шаге. Второй аргумент (здесь 0) — это стартовое значение.

Эти методы отлично сочетаются в цепочке. И вот тут такой стиль начинает по-настоящему окупаться:

index.js
Output
Click Run to see the output here.

Читается сверху вниз: оставляем оплаченные заказы, вытаскиваем цену, суммируем. Никаких циклов, никаких изменяемых счётчиков, никаких ошибок на единицу.

Функция, возвращающая функцию

Вторая половина истории про функции высшего порядка в javascript. Функция, которая собирает и возвращает другую функцию:

index.js
Output
Click Run to see the output here.

multiplyBy(2) выполняется один раз и возвращает совершенно новую функцию. Эта новая функция по-прежнему «помнит» factor — это и есть замыкание, и ему будет посвящена отдельная страница. А пока запомните главное: вызывая multiplyBy с разными аргументами, вы получаете разные специализированные функции, построенные по одному шаблону.

Такой приём встречается повсюду:

index.js
Output
Click Run to see the output here.

Одно определение — и сразу две переиспользуемые функции. Гораздо удобнее, чем писать warn и info руками и потом следить, чтобы они не разъехались.

Именованные функции против встроенных колбэков

Колбэк-функцию в JavaScript можно передать как стрелочную прямо на месте, а можно — по имени. Оба варианта рабочие, выбирайте тот, который читается понятнее:

index.js
Output
Click Run to see the output here.

Когда вы передаёте isEven (без скобок), вы передаёте саму функцию. А вот если добавить (), функция тут же вызовется и передастся её результат — классическая ошибка новичков:

nums.filter(isEven);     // правильно: передаём функцию
nums.filter(isEven());   // неправильно: вызываем isEven без аргументов и передаём результат

Если колбэк разрастается больше чем на пару строк, вынесите его в отдельную именованную функцию. Код вокруг от этого обычно только выигрывает.

Разбираем на конкретном примере

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

index.js
Output
Click Run to see the output here.

У каждого хелпера — одна задача. У каждого метода массива — одно преобразование. Цепочка читается как описание того, что вам нужно, а не того, как это зациклить.

Когда не стоит их использовать

Методы высшего порядка — штука классная, но заменять ими обычный цикл подходит не всегда:

  • Если нужно выйти из перебора на полпути, обычный for или for...of с break будет понятнее, чем попытки как-то «вырваться» из forEach.
  • Если внутри колбэка асинхронный код, map и forEach его не дождутся. Используйте for...of с await или связку Promise.all с map.
  • Если колбэк мутирует общее состояние — вы уходите от сильных сторон этого стиля. Лучше взять обычный цикл или переписать так, чтобы возвращались новые значения.

Там, где они уместны, map, filter и reduce убирают из повседневного кода почти весь циклический бойлерплейт. А если пихать их везде подряд — читаемость страдает. Выбирайте инструмент, который яснее всего передаёт замысел.

Дальше: объекты

Функции — не единственные значения, на которых строится код. Объекты в JavaScript — главная рабочая лошадка для того, чтобы собирать связанные данные и поведение вместе. И именно из них, кстати, состояли почти все массивы, которые вы только что фильтровали и маппили. Об этом — следующая страница.

Часто задаваемые вопросы

Что такое функция высшего порядка в JavaScript?

Это функция, которая делает хотя бы одно из двух: принимает другую функцию как аргумент или возвращает функцию в качестве результата. Array.prototype.map, setTimeout и addEventListener — все они являются функциями высшего порядка: вы передаёте им колбэк, а они сами его вызывают в нужный момент.

Чем отличаются map, filter и reduce?

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

Зачем возвращать функцию из другой функции?

Чтобы делать маленькие настраиваемые хелперы и не дублировать логику. Например, multiplyBy(n) возвращает новую функцию, которая умножает на n: из одного определения вы получаете сразу multiplyBy(2) и multiplyBy(10). Этот паттерн построен на замыканиях и постоянно встречается в обработчиках событий, middleware и утилитных библиотеках.

Учитесь программировать с Coddy

НАЧАТЬ