13 советов, как сделать код на JavaScript качественнее и быстрее

10 лет назад компания Amazon подсчитала, что 100 миллисекунд задержки стоили ей 1% выручки с продаж. Аналогичным образом в Google обнаружили, что дополнительные 500 миллисекунд на генерацию поисковых страниц сократили их трафик на 20%, на столько же урезав потенциальный доход от рекламы. Оказывается, скорость действительно решает всё. Мы публикуем перевод статьи британского разработчика Брета Кэмерона, в которой он дает 13 практических советов для увеличения скорости работы JavaScript-кода.

Меньше — лучше

Самый быстрый код — это код, который никогда не будет запущен

1. Удалите ненужные функции

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

2. Избегайте ненужных шагов

Оценочный тест

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

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

Реже — лучше

Если вы не можете удалить фрагмент кода, постарайтесь выполнять его реже 

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

3. Циклы должны завершаться как можно раньше

Оценочный тест

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

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

Стоит также помнить, что вложенные циклы можно разрывать с помощью меток. Это позволяет привязать оператор break или continue к определенному циклу:

4. Предвычисляйте, если возможно

Оценочный тест

Возьмем следующую функцию, в нашей программе мы хотим её вызвать несколько раз:

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

Теперь массивы «света» и «тьмы» будут создаваться всего один раз. То же самое относится к вложенным функциям. Например:

Каждый раз, когда мы запускаем doSomething, вложенная функция создается с нуля. Замыкание поможет и здесь. Если мы возвращаем функцию, doSomethingElse остается закрытой, но создается всего один раз:

5. Минимизируйте количество операций в коде

Оценочный тест

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

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

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

Ключевой момент — обеспечить выполнение операций в наилучшем порядке.

6. Изучите О-нотацию 

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

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

Ускоряйте исполнение

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

7. Используйте встроенные инструменты

Оценочный тест

Для тех, кто имеет опыт работы с компиляторами и низкоуровневыми языками, этот момент может показаться очевидным. Если нужные вам инструменты уже содержатся в самом JavaScript «из коробки», используйте именно их.

Код компилятора оптимизирован с точки зрения производительности конкретных методов и типов объектов. Кроме того, базовым языком для JavaScript служит C++. Если у вас не очень специфичный случай, вероятность того, что ваша собственная реализация JavaScript превзойдет существующие методы, крайне мала.

Чтобы проверить, создадим собственную JavaScript-реализацию Array.prototype.map:

Теперь давайте создадим массив из 100 случайных чисел от 1 до 100:

Даже если мы хотим выполнить простую операцию, например, умножить каждое целое число в массиве на два, мы всё равно увидим разницу в производительности:

В моих тестах новая функция map JavaScript оказалась примерно на 65% медленнее, чем Array.prototype.map. Чтобы посмотреть исходный код реализации Array.prototype.map движка V8, кликните сюда. А чтобы провести тесты самому, кликните по ссылке на оценочный тест.

8. Используйте объекты, наиболее подходящие для конкретной задачи

Что эффективнее:

— добавлять значения в коллекцию Set или в массив с помощью push()?

— добавлять записи в коллекцию Map или в обычный объект?

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

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

9. Не забудьте про память

Будучи высокоуровневым языком, JavaScript учитывает нюансы более низких уровней. Один из таких нюансов — управление памятью. JavaScript использует систему сбора «мусора» для высвобождения данных из памяти (если только явно не прописать в коде, что эти данные всё ещё нужны).

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

Например, у функций Set и Map есть так называемые слабые вариации (WeakSet и WeakMap). Они содержат «слабые» ссылки на объекты. Они не используют тип enumerable, но они предотвращают утечку памяти, так как позволяют отправлять в «мусор» не упомянутые в коде значения.

Вы также можете лучше контролировать распределение памяти, используя объекты TypedArray, представленные в обновлении JavaScript ES2017. Например, Int8Array может принимать значения от –128 до 127 и имеет размер всего в один байт. Стоит отметить, однако, что прирост производительности при использовании объектов TypedArray может быть очень мал: сравнение обычного массива и Uint32Array показывает небольшое улучшение производительности записи, но незначительное или нулевое улучшение производительности чтения (спасибо Крису Ху за оба теста).

Поняв низкоуровневый язык программирования, вы сможете быстрее и лучше писать код на JavaScript. Об этом я пишу подробнее в своей статье «Как C++ может помочь JavaScript-разработчикам».

10. Используйте мономорфные операции, если возможно

Оценочный тест 1: мономорфные операции vs полиморфные

Оценочный тест 2: один аргумент функции vs два

Если присвоить переменной a значение 2 с помощью const, то её можно считать полиморфной (ее можно изменить). Напротив, если мы будем непосредственно использовать числовое значение 2, то его можно считать мономорфным (его значение фиксировано).

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

Если запустить функцию multiply(2, 3), она завершится примерно на 1% быстрее, чем если запустить такой код:

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

Как я отметил ранее, улучшение производительности невелико (в моих тестах около 2%). Но если такое улучшение может быть использовано многократно в большой кодовой базе, то стоит подумать об этом. Как правило, вводить аргументы стоит тогда, когда значение динамическоe, а переменные вводятся только тогда, когда будут использоваться несколько раз.

11. Избегайте оператора delete

Оценочный тест 1: удаление свойств из объекта vs неопределенные свойства

Оценочный тест 2: оператор delete vs Map.prototype.delete

Оператор delete используется для удаления содержания из объекта. Может показаться, что он вам необходим, но если вы можете обойтись без него, то сделайте это. В движке V8 есть паттерн скрытых классов, и delete лишает вас преимуществ этого паттерна, делая объект обычным и медленным. А операции с медленными объектами — всё верно — выполняются медленнее.

В зависимости от ваших потребностей можно определить нежелательное свойство как неопределенное и, возможно, этого будет достаточно:

Я видел в интернете предположения, что было бы быстрее создать копию первоначального объекта без определенных свойств, используя следующие функции:

Однако в моих тестах функция, описанная выше, и некоторые другие работали даже медленнее, чем оператор delete. Кроме того, такие функции менее удобочитаемые, чем delete obj.a или obj.a = undefined.

В поисках альтернативы подумайте, можно ли использовать Map вместо объекта, так как Map.prototype.delete работает быстрее, чем оператор delete.

Откладывайте исполнение

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

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

12. Используйте асинхронный код для предотвращения блокировки потока

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

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

Кроме того, многие модули (например, файловая система Node.js) имеют асинхронные и синхронные варианты некоторых функций (например, fs. writeFile() и fs.writeFileSync()). В обычных условиях придерживайтесь стандартного асинхронного подхода.

13. Используйте разделение кода

Если вы используете JavaScript на стороне клиента, ваши приоритеты должны быть сосредоточены в визуальном быстродействии. Главный оценочный критерий — это First Contentful Paint (FCP), время, за которое появляется первый полезный для пользователя контент в DOM-элементах.

Разделение кода — один из лучших способов улучшить ситуацию. Вместо того, чтобы подавать ваш JavaScript-код одним большим файлом, подумайте о том, чтобы разделить его на более мелкие фрагменты. Как вы будете разбивать код, зависит от того, используете ли вы один из фреймворков (React, Angular, Vue) или обходитесь стандартными средствами самого JavaScript. 

Tree shaking — связанная с описанным кодом тактика статического анализа всего кода и исключения того, что на самом деле не используется. Чтобы узнать больше, я рекомендую эту статью от Google. Не забывайте оптимизировать свой код!

Заключение

Тестирование — лучший способ проверить, удалось ли вам оптимизировать код. В этой статье я привожу примеры кода, используя https://jsperf.com, но можно проверять и меньшие сегменты коды здесь:

— http://jsben.ch

— https://jsbench.me

— Ваша собственная консоль, через функции console.time() и console.timeEnd().

Что касается проверки производительности веб-приложений, отличной отправной точкой являются разделы Chrome Dev Tools про сети и производительность. Также рекомендую расширение Lighthouse от Google.

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

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

14 функций JavaScript, которые вы должны понимать

Востребованность языка JavaScript с каждым годом неумолимо растет. В 2019  этот язык уступил Java всего о,14% доли на украинском рынке. В 2020 году число вакансий в категории  JavaScript значительно увеличится, поэтому можете уже редактировать ваше резюме. А прежде чем идти на собеседование, вы должны понимать и уметь написать эти 14 функции JavaScript.

1 Определение конкретного типа любого объекта

Как мы знаем, в JavaScript есть пять примитивных типов данных и объектный тип данных. Но знаете ли вы, что объектный тип данных можно подразделить на множество начальных типов? Объект может быть массивом, функцией, картой и т. д. Если мы хотим получить конкретный тип объекта, что нам делать?

Код:

Объяснение:

ECMAScript имеет следующие правила:

Для разных объектов разные результаты будут возвращены при вызове Object.prototype.toString ().

Кроме того, возвращаемое значение Object.prototype.toString () всегда имеет формат ‘[object’ + ‘tag’ + ‘]’. Если нам нужен только средний тег, мы можем удалить символы с обеих сторон с помощью регулярного выражения или String. prototype.slice ().

Пример: 

toRawType(null) 
// "Null"toRawType(/sdfsd/) 
//"RegExp"

 

2 Результаты расчета функции кэширования

Если есть такая функция:

function computed(str) {  
  // Suppose the calculation in the funtion is very time consuming    
console.log('2000s have passed')   
 return 'a result'
}

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

Код:

Пример: 

 

 

3 Реализация Array.prototype.map

Это полезный встроенный метод в JavaScript, но вы должны иметь возможность реализовать эту функцию самостоятельно.

Код:

Пример: 

 

4 Реализация Array.prototype.filter

Это полезный встроенный метод в JavaScript, но вы должны иметь возможность реализовать эту функцию самостоятельно.

Код:

Пример: 

 

 5 Реализация Array.prototype.some

Это полезный встроенный метод в JavaScript, но вы должны иметь возможность реализовать эту функцию самостоятельно.

Код:

Пример:

 

6 Реализация Array.prototype.reduce

Это полезный встроенный метод в JavaScript, но вы должны иметь возможность реализовать эту функцию самостоятельно.

Код:

Пример:

 

7 Реализация Array.prototype.flat.

Код:

Пример:

 

8 Каррирование

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

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

Именно тогда мы превращаем вызов функции add (1,2,3) в add (1) (2) (3). Используя эту технику, маленький кусочек можно легко настроить и использовать повторно.

Почему это полезно?

  • Карринг помогает избежать повторной передачи одной и той же переменной.
  • Это помогает создать функцию более высокого порядка. Это чрезвычайно полезно при обработке событий.
  • Маленькие куски можно легко настроить и использовать повторно.

Давайте посмотрим на простую функцию добавления. Он принимает три операнда в качестве аргументов и возвращает сумму всех трех в качестве результата.

function add(a,b,c){
 return a + b + c;
}

Вы можете вызывать несколько (с нечетными результатами) или много (лишние аргументы игнорируются).

add(1,2,3) --> 6 
add(1,2) --> NaN
add(1,2,3,4) --> 6 //Extra parameters will be ignored.

Как преобразовать существующую функцию в curry?

Код:

Пример:

 

9 Debouncing

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

Предположим, пользователь хочет получить «Учебный комплект Tutorix». Он вводит каждый символ продукта в строке поиска. После ввода каждого символа происходит вызов API от браузера к серверу, чтобы получить нужный продукт. Поскольку ему нужен «учебный комплект Tutorix», пользователь должен сделать 24 вызовов Api из браузера на сервер. Подумайте о сценарии, который, когда миллионы людей проводят одинаковый поиск, вызывает миллиарды Api. Поэтому одновременное обращение к миллиардам Api определенно приведет к снижению производительности браузера. Чтобы уменьшить этот недостаток, на сцену выходит Debouncing.

В этом случае Debouncing установит интервал времени, например, 2 секунды, между двумя нажатиями клавиш. Если время между двумя нажатиями клавиш превышает 2 секунды, происходит только вызов Api. В течение этих 2 секунд пользователь может набрать хотя бы несколько символов, уменьшая количество этих вызовов Api. Поскольку количество вызовов Api уменьшилось, производительность браузера будет увеличена. Необходимо учитывать, что функция Debouncing обновляется при каждом нажатии клавиши.

Код:

 

10 Throttling

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

Код:

 

11 “Ленивая загрузка” изображений

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

Код:

 

12 Перемешивание массива

Часто возникает необходимость перемешать массив.

Код:

Пример:

 

13 Singleton

Singleton паттерн ограничивает количество экземпляров определенного объекта только одним. Этот единственный экземпляр называется синглтоном.

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

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

Код:

Пример:

 

14 Реализация JSON.stringify

Это полезный встроенный метод в JavaScript, но вы должны иметь возможность реализовать эту функцию самостоятельно.

Код:

Пример:

Оригинал статьи на medium.com

Метод JavaScript Array.Map() · Debbie Codes

Прежде чем мы углубимся в метод .map() , давайте просто вспомним, что такое массив. Массив — это структура данных, содержащая группу элементов. Думайте об этом как о большой коробке, внутри которой есть несколько меньших коробок, которые можно найти по их индексу. Таким образом, ящик в позиции 0 будет первым ящиком, а ящик в позиции 1 будет вторым ящиком. Внутри этих меньших блоков у нас может быть строка текста, чисел или объектов.

 const people = ['первый элемент', 'второй элемент'] 
 const people = [ { firstName: 'Debbie', lastName: 'O\'Brien' }, { firstName: 'Jake', lastName: 'Dohm ' }] 

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

Метод .map() принимает функцию, которая принимает 3 аргумента. Первое — текущее значение, второе — индекс, а третье — исходный массив, который мы итерируем.

 const name = people.map(function (currentValue, index, allPeople) {}) 

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

 const name = people.map(function (person) {}) 

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

 const name = people.map((person) => {}) 

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

 const name = people.map((person) => { return person}) 

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

 const name = people.map((person) => { return 'Debbie'}) 

Обычно мы хотим изменить данные или добавить к ним. Мы могли бы создать переменную с именем fullName и объединить значения firstName и lastName.

 const names = people.map((person) => { return { fullName: `${person.firstName} ${person.lastName}` }}) 

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

 константные имена = people.map((person) => { return { fullName: `${person.firstName} ${person.lastName}`, firstName: person.firstName, lastName: person.lastName }}) 

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

 константные имена = people.map((person) => { return { fullName: `${person.firstName} ${person.lastName}`, ...person }}) 

Вот и все. Теперь у нас есть новый массив имен с полным именем плюс имя и фамилия. Мы можем добавить больше ключей к объектам в нашем массиве людей, и мы получим эти новые ключи без необходимости что-либо изменять благодаря использованию оператора распространения. И, конечно же, мы могли бы добавить более одного элемента в массив имен, если бы захотели. Получайте удовольствие от .map() .

Не стесняйтесь экспериментировать с созданной мной кодовой ручкой.

  • Эта статья вдохновлена ​​видео Джейка Дома, которое я рекомендую вам посмотреть.
  • Чтобы узнать больше об этом, ознакомьтесь с документацией MDn для Array.prototype.map()
  • Ознакомьтесь с моим сообщением о методе фильтрации массива
  • Ознакомьтесь с моим сообщением о методе сопоставления массива

Виртуальный мир

Добавление баннера согласия на использование файлов cookie

Использование map() вместо циклов for()

Функциональное программирование на JavaScript — ваш друг

Опубликовано в

·

Чтение: 4 мин.

· 90 007 24 февраля, 2022

Предварительные знания

Прежде чем я начну, я расскажу вам, как работает функция .map() . Если вы знакомы только с циклами for() в JavaScript, эта статья потребует от вас понимания синтаксиса выражения функции стрелки (также известного как функции «толстой стрелки»).

Если будет достаточно спроса, я могу создать отдельный учебник, чтобы объяснить это подробнее. Дай мне знать в комментариях.

Приступим

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

Но я отвлекся.

В карьере каждого программиста был момент, когда нас учили, что императивное программирование (обычно как предшественник объектно-ориентированного программирования) было окончательным методом создания пригодного для использования программного обеспечения.

В некоторых случаях, таких как мой, ваше восприятие таких вещей постепенно начинает меняться. Мое восприятие и «стиль программирования» изменились с тех пор, как друг познакомил меня с функциональным программированием около 6 лет назад.

Я не утверждаю, что полностью понимаю монады или некоторые другие концепции из этой парадигмы, но одна вещь стала для меня ясной:

Чистые функции значительно упрощают чтение кода и написание автоматических тестов.

Ладно, хватит болтать. Давайте перейдем к реальному коду, не так ли?

Вы пыхтите вместе со своим кодом и получаете массив объектов, где каждый объект представляет собой

Person . Каждый Person содержит 3 свойства: id , name и emailAddress . Вам необходимо создать массив, содержащий только значения id от каждого Person в исходном массиве.

Вероятно, вы написали то, что вы написали, при условии, что вы использовали подход императивного программирования:

Принципиальная концепция, которая выделяется в каждом подобном сценарии, заключается в следующем: вы хотите создать новый массив точно такой же длины, как и предыдущий массив, но данные в новом массиве отличаются (так или иначе) и вы не хотите вносить какие-либо изменения в исходный массив.

Теперь вы нашли идеальный сценарий для функционального программирования.

Давайте посмотрим, как бы мы написали тот же код, что и выше, используя функцию Array. prototype.map() (не путать с объектом Map).

Когда я впервые увидел, как кто-то использует функцию .map() в качестве замены того, как я написал бы цикл for() выше, я растерялся. На самом деле есть еще один способ написать функцию .map() , который вы бы не стали делать в реальной ситуации, но он помогает объяснить, что здесь происходит.

Теперь я уверен, что вы узнаете фрагмент кода

listOfPeople[i].id из примера цикла for() . Чтобы объяснить это далее, .map() функция «зацикливается» на каждом элементе массива и назначает элемент (т.е. человек во втором и третьем примерах) и текущий индекс (т.е. i в первом и третьем примерах) как параметры функции, которую мы определяем.

Функция .map() требует, чтобы функция возвращала значение (например, person.id во втором примере и listOfPeople[i].id в третьем примере) для индекса, чтобы она могла чтобы присвоить это значение тому же индексу в новом массиве (т.