Статьи

Использование Map и Reduce в функциональном JavaScript

Эта статья была рецензирована Панайотисом Велисаракосом , Тимом Севериеном и Дэном Принсом . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

Со всеми разговорами о рабочих процессах, которые поддерживают удивительные новые функции в ECMAScript 6, легко забыть, что ECMAScript 5 принес нам несколько отличных инструментов для поддержки функционального программирования на JavaScript, которые мы можем использовать сегодня. Среди них встроенные методы map () и redu () базового объекта JavaScript Array

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

Производительность: предостережение

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

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

Также стоит учитывать, что использование таких методов, как map()reduce() Если у меня нет проблем с производительностью, я предпочитаю кодировать оптимистично и сохранять настройки производительности, которые делают мой код менее привлекательным в моем заднем кармане на случай необходимости.

Использование карты

Отображение является фундаментальным методом функционального программирования для работы со всеми элементами в массиве и создания другого массива такой же длины с преобразованным содержимым.

Чтобы сделать это немного более конкретным, давайте предложим простой пример использования. Например, представьте, что у вас есть массив слов, и вам нужно преобразовать его в массив, содержащий длину каждого слова. (Я знаю, что это не та сложная ракетостроение, которую вам часто приходится делать для вашего сложного приложения, но понимание того, как оно работает в простом случае, подобном этому, поможет вам применить его в тех случаях, когда это может повысить реальную ценность вашего кода) ,

Вы, наверное, уже знаете, как сделать то, что я только что описал, используя цикл for Это может выглядеть примерно так:

 var animals = ["cat","dog","fish"];
var lengths = [];
var item;
var count;
var loops = animals.length;
for (count = 0; count < loops; count++){
  item = animals[count];
  lengths.push(item.length);
}
console.log(lengths); //[3, 3, 4]

Все, что мы сделали, это определили несколько переменных: массив с именем animalslengthsitem Мы создали цикл forcountloops Затем мы повторили каждый из пунктов до длины массива for Для каждого мы вычислили длину и поместили ее в массив animals

Примечание: возможно, мы могли бы сделать это немного более кратко без переменной item, поместив длину lengthsanimals[count] Это сэкономило бы нам немного кода, но также сделало бы вещи менее читабельными, даже для этого очень простого примера. Точно так же, чтобы сделать это более производительным, но немного менее простым, мы могли бы использовать известную длину массива lengthsanimalslengths Все зависит от того, как вы собираетесь использовать код в реальном мире.

В этом подходе нет ничего технически неправильного. Он должен работать в любом стандартном движке JavaScript, и он сделает свою работу. Но как только вы узнаете, как использовать new Array(animals.length)

Позвольте мне показать вам, как мы можем подойти к этому с помощью map()

 map()

В этом случае еще раз мы начали с переменной для нашего var animals = ["cat","dog","fish"];
var lengths = animals.map(function(animal) {
return animal.length;
});
console.log(lengths); //[3, 3, 4]
Однако единственной другой переменной, которую мы объявили, были animalslengths Эта анонимная функция выполняла операцию над каждым животным, возвращая длину. В результате animalslengths

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

Еще одним преимуществом этого подхода является то, что у нас есть возможность улучшить его универсальность, разделив именованную функцию, производя более чистый код в процессе. Анонимные встроенные функции могут выглядеть беспорядочно и затруднять повторное использование кода. Мы могли бы определить именованную animals

 getLength()

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

Что такое Функтор?

Интересно, что, добавив отображение в объект массива, ECMAScript 5 превратил базовый тип массива в полноценный функтор, что делает функциональное программирование еще более доступным для всех нас.

Согласно классическим определениям функционального программирования, функтор отвечает трем критериям:

  1. Он содержит набор значений
  2. Он реализует функцию карты для работы с каждым элементом
  3. Его функция map возвращает функтор того же размера

Это то, что нужно бросить на следующей встрече JavaScript.

Если вы хотите узнать больше о функторах, посмотрите это отличное видео от Mattias Petter Johansson.

Использование Уменьшить

Метод redu () также является новым в ECMAScript 5 и похож на var animals = ["cat","dog","fish"];
function getLength(word) {
return word.length;
}
console.log(animals.map(getLength)); //[3, 3, 4]
map()
Например, представьте, что вы хотите получить сумму длин всех слов в нашем массиве reduce() Вы можете начать делать что-то вроде этого:

 animals

После того, как мы определим наш начальный массив, мы создадим переменную var animals = ["cat","dog","fish"];
var total = 0;
var item;
for (var count = 0, loops = animals.length; count < loops; count++){
item = animals[count];
total += item.length;
}
console.log(total); //10
Мы также создаем элемент переменной для хранения каждой итерации массива totalitemanimalsfor Затем мы запускаем цикл countloopsfor Наконец мы добавляем длину каждого элемента к нашему итогу.

Опять же, нет ничего технически неправильного в этом подходе. Мы начинаем с массива и в итоге получаем результат. Но с помощью метода animals

 item

Здесь происходит то, что мы определяем новую переменную reduce()var animals = ["cat","dog","fish"];
var total = animals.reduce(function(sum, word) {
return sum + word.length;
}, 0);
console.log(total);
Сокращение проходит через каждый элемент в массиве, выполняет функцию для этого элемента и добавляет его к промежуточному итогу, который передается на следующую итерацию. Здесь наша встроенная функция принимает два параметра: текущую сумму и слово, которое в данный момент обрабатывается из массива. Функция добавляет текущее значение total

Обратите внимание, что мы устанавливаем второй аргумент animalstotal Без этого второго аргумента метод Reduce все равно будет работать, но результат не обязательно будет таким, как вы ожидаете. (Попробуйте и посмотрите, сможете ли вы вывести логику, которую использует JavaScript, когда промежуточная сумма не указана.)

Это может выглядеть немного сложнее, чем нужно, из-за интегрированного определения встроенной функции при вызове метода redu reduce() Давайте сделаем это снова, но давайте сначала определим именованную функцию вместо использования анонимной встроенной функции:

 total

Это немного дольше, но дольше не всегда плохо. Если посмотреть на это таким образом, то станет понятнее, что происходит с методом Reduce.

Метод reduce() В этом случае мы передаем имя новой функции var animals = ["cat","dog","fish"];
var addLength = function(sum, word) {
return sum + word.length;
};
var total = animals.reduce(addLength, 0);
console.log(total);
reduce()
Мы создали addLengthaddLength()

Вывод

Привычное использование map()reduce()

Методы map()reduce() Разработайте с функциональными методами и оцените влияние в реальном мире, прежде чем решить, подходят ли map()reduce()