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():


Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: