Метод map()
створює новий масив з результатами виклику наданої функції на кожному елементі масиву, який викликав метод.
Синтаксис
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Повернути елемент нового масиву new_array
}[, thisArg])
Параметри
callback
- Функція, яка повертає елемент нового масиву. Приймає три аргументи:
-
currentValue
- Поточний елемент масиву.
index
Optional- Індекс поточного елементу масиву
array
Optional- Сам масив, на якому був викликаний
map
.
thisArg
Optional- Значення, що буде використане як
this
при виконанніcallback
.
Значення, що повертається (return value)
Новий масив, кожен елемент якого є результатом функції callback
.
Опис
Метод map
викликає передану callback-функцію
один раз для кожного елементу масиву, в заданому порядку, та створює новий масив з результатів. callback
викликаться тільки для індексів яким відповідають значення, включно з undefined. Функція не викликається для елеметів значення яких відсутні (мається на увазі, індекси які не були явно задані, які були видалені або яким не було присвоєне значення).
Остільки map
створює новий масив, викликати його, якщо ви не збираєтесь використовувати повернений масив, є антишаблоном; скористайтесь натомість forEach
або for-of
. Ознаки того, що вам не підходить метод map
: А) Ви не використовуєте масив, який він повертає, і/або Б) Ви не повертаєте значення у функції callback
.
callback
викликається з трьома аргументами: значення елемента, індекс елемента, та масив на якому операцію було викликано.
Якщо thisArg
параметр переданий в map
, він буде використовуватись як ключове слово this для callback-функції
. В іншому випадку, значення undefined
буде використане як this
. Значення this, яке спостерігається в callback-функції, в кінцевому рахунку визначається згідно звичайних правил для визначення this, видимого з функції.
map
не змінює масив, на якому був викликаний (хоча callback
, якщо був викликаний, може змінити).
Діапазон елементів, які обробляє метод map, визначений до того як callback-функція буде визвана вперше. Елементи які будуть додані до масиву після виклику map, не будуть оброблені callback-функцією. Якщо існуючі в масиві елементи змінені або видалені, їхні значення при потраплянні в callback-функцію, будуть такими якими вони є на той час коли map обробляє їх. Елементи які були видалені до і після того як map був визваний, ігноруються.
Згідно з алгоритмом, визначеним у специфікації, якщо масив на якому було викликано map, розріджений, то масив на виході теж буде розрідженим, залишаючи ті ж самі індекси пустими.
Приклади
Перетворення масиву з числами в масив квадратних коренів
Наступний код бере масив з числами і створює новий масив, який складається з квадратних коренів чисел з першого масиву.
var numbers = [1, 4, 9];
var roots = numbers.map(function(num) {
return Math.sqrt(num)
});
// roots тепер [1, 2, 3]
// numbers залишається [1, 4, 9]
Використання map для переформатування об'єктів в масиві
Наступний код використовує масив з об'єктами щоб створити новий масив з переформатованими об'єктами.
var kvArray = [{key:1, value:10},
{key:2, value:20},
{key:3, value: 30}];
var reformattedArray = kvArray.map(obj =>{
var rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
// reformattedArray тепер [{1:10}, {2:20}, {3:30}],
// kvArray залишається:
// [{key:1, value:10},
// {key:2, value:20},
// {key:3, value: 30}]
Перетворення масиву чисел використовуючи функцію з аргументом
Наступний код показує як map працює коли функція що потребує один аргумент, використовує його. Аргумент буде автоматично присвоєний з кожного елементу масиву коли map буде проходитись по оригінальному масиву..
var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
return num * 2;
});
// doubles тепер [2, 8, 18]
// numbers залишається [1, 4, 9]
Загальне використання map
Цей приклад показує як використовувати map на рядках (String
) щоб отримати масив байтів в ASCII кодуванні яке відображає значення літер:
var map = Array.prototype.map;
var a = map.call('Hello World', function(x) {
return x.charCodeAt(0);
});
// a тепер дорівнює [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
Загальне використання map
з querySelectorAll
Даний приклад показує, як виконати перебір набору об'єктів, отриманих методом querySelectorAll
. Тому що метод querySelectorAll
повертає NodeList, який є колекцією об'єктів.
У даному випадку ми повертаємо значення всіх вибраних опцій на екрані:
var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
return obj.value;
});
Це простіше зробити методом Array.from()
.
Хитрий спосіб використання
Загальноприйнято використовувати функцію callback з одним аргументом (елемент по якому проходиться функція). Деякі функції також використовуються з одним аргументом, хоча можуть приймати і більше додаткових аргументів, що не є обов'язковими. Це може призводити до неочікуваної поведінки.
Розглянемо:
["1", "2", "3"].map(parseInt);
Хоча можна було б очікувати [1, 2, 3]
, справжнім результатом буде [1, NaN, NaN]
.
Метод parseInt
часто використовується з одним аргументом, але приймає два. Перший - це вираз, а другий - основа системи числення для функції зворотного виклику. Array.prototype.map
передає 3 аргументи:
- елемент
- індекс
- масив
Третій аргумент ігнорується методом parseInt, але не другий, звідси й можлива плутанина. Ось стислий огляд кроків перебору:
// parseInt(string, radix) -> map(parseInt(value, index))
/* first iteration (index is 0): */ parseInt("1", 0); // 1
/* second iteration (index is 1): */ parseInt("2", 1); // NaN
/* third iteration (index is 2): */ parseInt("3", 2); // NaN
Поговоримо про рішення.
function returnInt(element) {
return parseInt(element, 10);
}
['1', '2', '3'].map(returnInt); // [1, 2, 3]
// Результатом є масив чисел (як і очікувалось)
// Те саме, але з використанням лаконічного стрілкового синтаксису
['1', '2', '3'].map( str => parseInt(str) );
// Простіший спосіб досягти вищевказаного
['1', '2', '3'].map(Number); // [1, 2, 3]
// На відміну від parseInt(), Number() також поверне десятковий або експоненціальний запис:
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
// Для порівняння, якщо використати parseInt() для попереднього масиву:
['1.1', '2.2e2', '3e300'].map( str => parseInt(str) ); // [1, 2, 3]
Ще один варіант результату метода map, що викликається з parseInt в якості параметра, виглядає наступним чином:
var xs = ['10', '10', '10'];
xs = xs.map(parseInt);
console.log(xs);
// Результат 10,NaN,2 може бути неочікуваним, з огляду вищеописане.
Масив містить значення undefined
Коли повертається undefined або нічого не повертається:
var numbers = [1, 2, 3, 4];
var filteredNumbers = numbers.map(function(num, index) {
if(index < 2) {
return num;
}
});
// filteredNumbers дорівнює [1, 2, undefined, undefined]
// numbers досі дорівнює [1, 2, 3, 4]
Поліфіл
Метод map
був доданий до ECMA-262 стандарту в 5-тій редакції; тому він може бути присутнім не у всіх реалізаціях стандарту. Ви можете обійти це, вставляючи наступний код на початок вашого скритпу, дозволяючи використовувати map в реалізаціях які ще його не підтримують. Цей алгоритм є точно таким який вказаний в ECMA-262, 5му виданні, передбачаючи що Object
, TypeError
, і Array
мають свої власні значення і що callback.call
обчислює початкове значення
.Function.prototype.call
// Функціональні кроки ECMA-262, версія 5, 15.4.4.19
// Довідка: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
// 1. Нехай O дорівнює результату виклику ToObject з |this|
// в якості аргументу.
var O = Object(this);
// 2. Нехай lenValue дорівнює результату виклику внутрішнього методу O
// Get з аргументом "length".
// 3. Нехай len дорівнює ToUint32(lenValue).
var len = O.length >>> 0;
// 4. Якщо IsCallable(callback) дорівнює false, викинути виняток TypeError.
// Див.: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. Якщо надано thisArg, нехай T дорівнює thisArg; інакше нехай T дорівнює undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Нехай A дорівнює новому масиву, створеному виразом new Array(len),
// де Array - це стандартний вбудований конструктор з таким ім'ям,
// а len дорівнює значенню len.
A = new Array(len);
// 7. Нехай k дорівнює 0
k = 0;
// 8. Повторювати, доки k < len
while (k < len) {
var kValue, mappedValue;
// а. Нехай Pk дорівнює ToString(k).
// Цей метод неявно застосовується до лівого операнда оператора in
// б. Нехай kPresent дорівнює результату виклику внутрішнього методу O
// HasProperty з аргументом Pk.
// Цей крок можна об'єднати з в
// в. Якщо kPresent дорівнює true, тоді
if (k in O) {
// і. Нехай kValue дорівнює результату виклику внутрішнього методу O
// Get з аргументом Pk.
kValue = O[k];
// ii. Нехай mappedValue дорівнює результату виклику внутрішнього
// методу callback Call з T у якості значення this та списком
// аргументів, що містить kValue, k та O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Викликати внутрішній метод A DefineOwnProperty з аргументами
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// та false.
// У переглядачах, що підтримують Object.defineProperty, використовуйте:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// Для найкращої підтримки переглядачів, використовуйте:
A[k] = mappedValue;
}
// г. Збільшити k на 1.
k++;
}
// 9. повернути A
return A;
};
}
Специфікації
Специфікація | Статус | Коментар |
---|---|---|
ECMAScript 5.1 (ECMA-262) The definition of 'Array.prototype.map' in that specification. |
Standard |
Початкове визначення. Реалізоване в JavaScript 1.6. |
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'Array.prototype.map' in that specification. |
Standard | |
ECMAScript (ECMA-262) The definition of 'Array.prototype.map' in that specification. |
Living Standard |
Сумісність з веб-переглядачами
BCD tables only load in the browser