Статьи

Совет: прекрати писать циклы и начинай думать с помощью карт

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

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

Императив

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

 var mixedEmails = ['[email protected]', '[email protected]', '[email protected]']; function getEmailsInLowercase(emails) { var lowercaseEmails = []; for (var i = 0; i < emails.length; i++) { lowercaseEmails.push(emails[i].toLowerCase()); } return lowercaseEmails; } var validData = getEmailsInLowercase(mixedEmails); 

Этот подход работает, но он потребовал много усилий для достижения того, что на самом деле является простой и обычной операцией. Наша функция с циклом for кодирует так много деталей, что мы не собирались выражать. Несколько больных мест:

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

Это императивный подход к программированию. Мы диктуем машине, как она должна делать свою работу.

Смущенный

Мы хотим очистить предыдущий подход, поэтому мы добрались до функции map . Когда мы читаем любую документацию для функции map , мы видим такие слова, как «массив», «каждый» и «индекс». Это предполагает, что мы можем рассматривать map как немного менее церемонную for цикла, и мы действительно можем это сделать. Давайте изменим нашу оригинальную функцию.

 var mixedEmails = ['[email protected]', '[email protected]', '[email protected]']; function getEmailsInLowercase(emails) { var lowercaseEmails = []; emails.map(function(email) { lowercaseEmails.push(email.toLowerCase()); }); return lowercaseEmails; } var validData = getEmailsInLowercase(mixedEmails); 

Это работает и чище, чем подход цикла. Помимо меньшего количества символов в фрагменте кода, мы не говорим машине, как отслеживать индексы или в каком направлении она должна работать через наш список.

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

декларативный

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

 var mixedEmails = ['[email protected]', '[email protected]', '[email protected]']; function downcase(str) { return str.toLowerCase(); } var validData = mixedEmails.map(downcase); 

Не трудно утверждать, что это более читабельно для человека, и в этом суть программирования: выражать идеи другим людям , будь то другие разработчики или ваше будущее я. Приведенный выше фрагмент говорит: «Наши действительные данные — это наш смешанный список электронных писем, сопоставленный с функцией downcase ».

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

У этого подхода есть несколько дополнительных преимуществ. В произвольном порядке:

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

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

Поддержка браузера

Собственный метод map() определен в спецификации ECMAScript 5 и имеет хорошую поддержку браузера . Если вам требуется поддержка версии Internet Explorer ранее 9, вы можете ввести полифилл или использовать такие библиотеки, как Underscore или Lodash .

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

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

Завершение

Функциональный подход, заключающийся в разделении логики на простые чистые методы и применении этих методов к структурам данных, сделает ваш код более лаконичным, надежным и понятным. Концепция является общей, а более общие концепции позволяют более повторно использовать код. Умение думать таким образом улучшит не только ваш JavaScript, но и вашу работу с большинством других языков программирования; Вы можете применить этот подход в Ruby так же легко, как в Haskell.

Итак, в следующий раз, когда вы доберетесь до цикла for , пересмотрите. Имейте в виду, что структура данных, с которой вы начинаете, не обязательно должна быть плоским массивом; Вы можете начать с объекта, извлечь его значения, затем сопоставить функцию с этим и закончить, отсортировав полученный массив. Вы даже можете использовать библиотеку, такую ​​как Underscore, чтобы отобразить объект, сохраняя ключи .

Можете ли вы придумать более креативные способы использования функции map() ? Экспериментируйте и наблюдайте, как ваш код сокращается.