Эта статья была рецензирована Крисом Перри , Марком Таулером , Саймоном Кодрингтоном и Тимом Эвко . Спасибо всем рецензентам 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()
? Экспериментируйте и наблюдайте, как ваш код сокращается.