Map() в JavaScript: главный секрет метода
Редакция Highload разобралась, как и зачем использовать метод map() массива JavaScript, на реальных примерах.
Содержание:
1. Что представляет собой метод map() в JavaScript?
2. Синтаксис и параметры метода map() в JavaScript
2.1. Параметры
2.2. Возвращаемое значение
3. Примеры использования map() в JavaScript
3.1. С одним аргументом
3.2. С двумя аргументами
3.3. С тремя аргументами
4. Совместимость
5. Полифил
6. Когда не следует использовать map()
7. Дополнительные примеры
7.1. Преобразование строки в массив
7.2. Рендеринг списков с помощью React
7.3. Переформатирование массива
8. Сложности использования метода map()
Заключение
1. Что представляет собой метод map() в JavaScript?
Метод map()
массивов JavaScript
применяет указанную функцию последовательно к каждому элементу массива и создает новый массив из результатов.
const args = [1, 2, 3, 4]; const squares = args.map(x => x * x); console.log(squares)
Приведенный выше код возводит в квадрат элементы массива args
и создает из результатов новый массив squares
:
[1, 4, 9, 16]
Метод map()
не изменяет исходный массив и выполняет функцию только для тех элементов, которым присвоены значения (в том числе, undefined).
Метод map()
— это возможность ECMAScript5 (ES5). ES5 (JavaScript 2009) полностью поддерживается всеми современными браузерами.
2. Синтаксис и параметры метода map() в JavaScript
Для метода map()
используются такие сигнатуры:
// Сигнатура со стрелкой array.map((element) => { /* ... */ }) array.map((element, index) => { /* ... */ }) array.map((element, index, arr) => { /* ... */ }) // Функция обратного вызова array.map(callbackFn) array.map(callbackFn, thisValue) // Вложенная функция обратного вызова array.map(function(element) { /* ... */ }) array.map(function(element, index) { /* ... */ }) array.map(function(element, index, arr){ /* ... */ }) array.map(function(element, index, arr) { /* ... */ }, thisValue)
2.1. Параметры
array |
Исходный массив, к элементам которого будет применена функция. |
function() |
Обязательный.
Функция, которая будет применяться к каждому элементу массива. |
element |
Обязательный.
Значение текущего элемента. |
index |
Необязательный.
Индекс текущего элемента. |
arr |
Необязательный.
Массив текущего элемента. |
thisValue |
Необязательный.
Значение по умолчанию — Значение, которое передается функции в качестве |
2.2. Возвращаемое значение
Массив с результатами применения функции к каждому элементу исходного массива.
3. Примеры использования map() в JavaScript
3.1. С одним аргументом
array.map((element) => { /* ... */ })
Допустим, имеется список из топ-3 игр 2021 года. Требуется вывести названия игр и разработчиков. Для этого подойдут сигнатуры с одним аргументом. Вот как это можно сделать, используя сигнатуру map()
со стрелкой:
<!DOCTYPE html> <html> <head> <title> Метод map() массива JavaScript </title> </head> <body> <div id="root"></div> <script> var el = document.getElementById('root'); const top3_games = [ {name: "Deathloop", dev: "Arkane Studios"}, {name: "Halo Infinite", dev: "343 Industries"}, {name: "Metroid Dread", dev: "MercurySteam and Nintendo EPD"} ]; var game_list = top3_games.map(game => [game.name, game.dev].join(" by ")); el.innerHTML = JSON.stringify(game_list); </script> </body> </html>
В результате выполнения в элемент div
с идентификатором root
будет помещено следующее:
["Deathloop by Arkane Studios","Halo Infinite by 343 Industries","Metroid Dread by MercurySteam and Nintendo EPD"]
Тот же самый результат получим с использованием функции обратного вызова:
var game_list = top3_games.map(getInfo); function getInfo(game) { return [game.name, game.dev].join(" by "); }
и с помощью вложенной функции:
var game_list = top3_games.map(function(game) { return [game.name, game.dev].join(" by "); });
Запись со стрелкой — самая компактная, в то время как в отдельной функции можно гибко разместить крупный фрагмент кода.
3.2. С двумя аргументами
Теперь давайте выведем игры под порядковыми номерами. Для получения порядкового номера воспользуемся вторым аргументом функции: index
.
Для простоты возьмем пример сигнатуры со стрелкой и изменим строку, в которой применяется метод map()
:
var game_list = top3_games.map((game, index) => `${index + 1}. ${[game.name, game.dev].join(" by ")}`);
Теперь в начале каждого значения будет указан его порядковый номер, который на единицу больше индекса:
["1. Deathloop by Arkane Studios","2. Halo Infinite by 343 Industries","3. Metroid Dread by MercurySteam and Nintendo EPD"]
3.3. С тремя аргументами
В качестве третьего аргумента функции обратного вызова передается исходный массив. Мы можем использовать его, например, если нужно указать общее количество элементов:
var game_list = top3_games.map((game, index, arr) => `${index + 1} of ${arr.length}: ${[game.name, game.dev].join(" by ")}`);
В результате получим:
["1 of 3: Deathloop by Arkane Studios","2 of 3: Halo Infinite by 343 Industries","3 of 3: Metroid Dread by MercurySteam and Nintendo EPD"]
4. Совместимость
Метод map()
JavaScript поддерживается современными браузерами. В приведенной ниже таблице указаны версии браузеров и платформ, начиная с которых реализована его поддержка.
Браузеры
Chrome | 1 |
Edge | 12 |
Firefox | 1.5 |
IE | 9 |
Opera | 9.5 |
Safari | 3 |
Мобильные браузеры
WebView Android | 37 |
Chrome Android | 18 |
Firefox for Android | 4 |
Opera Android | 10.1 |
Safari on iOS | 1 |
Samsung Internet | 1.0 |
Платформы
Deno | 1.0 |
Node.js | 0.10.0 |
5. Полифил
Что делать, если используемая версия браузера не поддерживает метод map()
? В этом случае метод можно заменить полифилом. Полифил (англ. polyfill) — это фрагмент кода, реализующий новую функциональность в устаревших версиях браузеров, которые не поддерживают современные функции.
Полифил для Array.prototype.map
доступен в core-js. Но его можно написать и самостоятельно. Для этого нужно реализовать функцию, в которой:
- создается новый массив;
- в цикле делается проход по всем элементам исходного массива;
- для каждого из них вызывается указанная функция;
- результат функции добавляется в новый массив;
- после завершения цикла новый массив возвращается из функции.
<!DOCTYPE html> <html> <head> <title> Полифил для метода map() массива JavaScript </title> </head> <body> <div id="root"></div> <script> var el = document.getElementById('root'); Array.prototype.pfMap = function(callbackFn) { var result = []; for (var i = 0; i < this.length; i++) { result.push(callbackFn(this[i], i, this)); } return result; } var doubled = [1, 2, 3, 4, 5].pfMap(x => x * 2); el.innerHTML = JSON.stringify(doubled); </script> </body> </html>
В этом примере кода на странице выводится массив с удвоенными значениями элементов исходного массива:
[2,4,6,8,10]
6. Когда не следует использовать map()
map()
— один из методов, с помощью которых можно производить итерацию по массиву. Но существуют ситуации, когда использовать его нецелесообразно. Это случаи, когда:
- когда возвращаемый массив не используется;
- когда функция обратного вызова не возвращает значения.
В таких случаях рекомендуется пользоваться forEach и for...of
.
7. Дополнительные примеры
7.1. Преобразование строки в массив
Как уже упоминалось в разделе выше о полифилах, метод map()
— это метод прототипа массива Array.prototype.map
. Возможности JavaScript позволяют перенести этот метод в контекст строки и выполнить итерацию, перебрав ее по символам. Это можно сделать, вызвав map()
с помощью метода call()
.
<!DOCTYPE html> <html> <head> <title> Преобразование строки в массив </title> </head> <body> <div id="root"></div> <script> var el = document.getElementById('root'); const line = "Do or do not. There is no try."; const map = Array.prototype.map; const newLine = map.call(line, currentChar => { return `${currentChar}` }); el.innerHTML = JSON.stringify(newLine); </script> </body> </html>
В итоге каждый символ станет отдельным строковым элементом нового массива:
["D","o"," ","o","r"," ","d","o"," ","n","o","t","."," ","T","h","e","r","e"," ","i","s"," ","n","o"," ","t","r","y","."]
7.2. Рендеринг списков с помощью React
Метод map()
можно использовать для рендеринга списков, например в React. Вот содержимое JSX-файла компонента, который выводит слова из массива в виде списка:
import React from "react"; import ReactDOM from "react-dom"; const words = ["каждый", "охотник", "желает", "знать", "где", "сидит", "фазан"]; const WordList = () => ( <div> <ul>{words.map(word => <li key={word}> {word} </li>)}</ul> </div> ); ReactDOM.render(<WordList />, document.getElementById("root"));
Результат:
- каждый
- охотник
- желает
- знать
- где
- сидит
- фазан
7.3. Переформатирование массива
С помощью метода map()
можно изменить объекты внутри массива, по которому производится итерация. В приведенном ниже коде проводится преобразование массива, в котором указаны названия топ-3 языков программирования в 2021 году и годы их создания, в массив с названиями этих языков и их местами по популярности.
<!DOCTYPE html> <html> <head> <title> Переформатирование массива JavaScript с помощью map() </title> </head> <body> <div id="root"></div> <script> var el = document.getElementById('root'); const top3_languages = [ {name: "Python", year: 1991}, {name: "Java", year: 1995}, {name: "JavaScript", year: 1995} ]; var language_places = top3_languages.map(function(game, index) { var item = {}; item["place"] = index + 1; item["name"] = game.name; return item; }); el.innerHTML = JSON.stringify(language_places); </script> </body> </html>
Код выводит следующий массив:
[{"place":1,"name":"Python"},{"place":2,"name":"Java"},{"place":3,"name":"JavaScript"}]
8. Сложности использования метода map()
Методу map()
можно передавать не только пользовательские, но и встроенные функции. Передавая встроенные функции, следует обращать особое внимание на количество аргументов, которые они принимают. Рассмотрим следующий пример.
<!DOCTYPE html> <html> <head> <title> map() и встроенные функции </title> </head> <body> <div id="root"></div> <script> document.getElementById("root").innerHTML = ["5", "4", "3", "2", "1"].map(parseInt); </script> </body> </html>
На первый взгляд, этот код тривиален и должен выдать последовательность 5, 4, 3, 2, 1
, преобразовав строки в целые числа. Но фактически мы получаем значения 5, NaN, NaN, 2, 1
. Почему так происходит?
Метод parseInt
принимает два аргумента: строку и основание системы счисления. В то же время функция обратного вызова передает три значения: элемент, индекс и массив.
Посмотрим, что происходит при выполнении этого кода. Из трех значений, передаваемых функции parseInt
, последнее игнорируется. В качестве аргумента для преобразования передается 5, а в качестве основания системы счисления — 0 (индекс первого элемента). Когда parseInt
принимает второе значение 0, а строка не начинается с «0x
», используется десятичная система счисления. Поэтому в результате первое значение — 5.
Диапазон значений для основания системы счисления в parseInt
— числа от 2 до 36. Второе значение в массиве («4
») имеет индекс 1, который выходит за пределы допустимых значений второго параметра parseInt
. Поэтому в результате выполнения итерации для этого значения получаем NaN.
На следующей итерации будет передано основание 2, но в двоичной системе счисления нет цифры 3, и снова получаем NaN.
Далее получаем 2 и 1, потому что в троичной и четверичной системах есть эти цифры.
Существует несколько решений этой проблемы.
Можно создать «враппер» для parseInt
, который будет явно принимать один аргумент и передавать основание 10:
arr = ["5", "4", "3", "2", "1"]; function toInt(el){ return parseInt(el, 10); } document.getElementById("root").innerHTML = arr.map(toInt);
То же решение можно переписать с использованием сигнатуры со стрелкой:
document.getElementById("root").innerHTML = arr.map( str => parseInt(str) );
И третьим решением будет применение Number
:
document.getElementById("root").innerHTML = arr.map(Number);
Заключение
Метод map()
массива JavaScript пригодится, когда требуется применить определенную функцию к произвольному количеству аргументов и получить результаты в новом массиве. Его целесообразно использовать, когда нужно оставить исходный массив без изменений. В противном случае рекомендуется использовать другие средства, не создающие нового массива.
Как обычно, в заключение предлагаем посмотреть подробное видео про метод массива map()
:
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: