Map
Об'єкт Map
(відображення) містить пари ключ-значення і запам'ятовує порядок вставки ключів у об'єкт.
Будь-які значення (і об'єкти, і примітивні значення) можуть використовуватись і як ключ, і як значення.
Спробуйте його в дії
Опис
Об'єкти Map
є колекціями пар ключ-значення. Конкретний ключ в Map
може зустрітись лише раз; він неповторний в межах колекції Map
. Обхід об'єкта Map
відбувається за його парами ключ-значення: цикл for...of
на кожній ітерації поверне масив із двох елементів, [ключ, значення]
. Обхід відбувається в порядку додання, котрий відповідає порядкові, в якому кожна пара ключ-значення була вставлена у відображення методом set()
(тобто коли до виклику set()
у відображенні не було ключа з таким само значенням).
Специфікація вимагає, щоб відображення були реалізовані "так, щоб в середньому час доступу був сублінійним відносно числа елементів колекції". Таким чином, внутрішньо вони можуть бути представлені як геш-таблиця (з доступом O(1)), як дерево пошуку (з доступом O(log(N))) або будь-яка інша структура даних, поки складність доступу краща за O(N).
Рівність ключів
Рівність значень заснована на алгоритмі sameValueZero
. (Раніше використовувався алгоритм SameValue, котрий розглядав 0
і -0
як різні значення. Перевірте сумісність із браузерами.) Це означає, що значення NaN
вважається рівним іншому значенню NaN
(попри те, що NaN !== NaN
), а всі решта значень перевіряються на рівність згідно з семантикою оператора ===
.
Порівняння Object
та Map
Object
дуже схожий до Map
: обидві структури даних дають змогу задавати значення за ключами, отримувати ці значення, видаляти ключі та визначати, чи зберігається щось за певним ключем. З таких міркувань (а ще тому, що не існувало вбудованих альтернатив), історично Object
використовується як Map
.
Однак, є важливі відмінності, котрі роблять Map
бажаним у деяких випадках:
`Map` | `Object` | |
---|---|---|
Випадкові ключі |
Новостворений Map усталено не містить ніяких ключів. Він містить лише те, що було явно покладено в нього.
|
Примітка: Цю проблему можна обійти за допомогою |
Безпека |
Об'єкти Map безпечно використовувати з наданими користувачем ключами та значеннями.
|
Задання наданих користувачем пар ключ-значення на об'єкті |
Типи ключів |
Ключі в Map можуть мати будь-яке значення (включно з функціями, об'єктами та будь-якими примітивними значеннями).
|
Ключі в Object повинні мати тип або String , або Symbol .
|
Порядок ключів |
Ключі в |
Хоча ключі звичайного
Цей порядок було вперше визначено лише для власних властивостей в ECMAScript
2015; ECMAScript 2020 визначає також порядок успадкованих властивостей.
Проте зауважте, що жоден механізм не дозволяє перебрати всі властивості об'єкта; різні механізми охоплюють різні підмножини властивостей. Зокрема, ( |
Розмір |
Кількість елементів всередині Map легко отримується з його властивості size .
|
З'ясування кількості елементів усередині Object виконується в обхід, менш ефективно. Загальноприйнятий спосіб це зробити – через length масиву, поверненого з Object.keys() .
|
Перебирання |
Map — це
ітерований об'єкт, тож його елементи можна перебирати напряму.
|
Зауваження:
|
Швидкодія |
Краще працює при ситуаціях, коли необхідно часто додавати й видаляти пари ключ-значення. |
Не оптимізований для частого вставляння і видалення пар ключ-значення. |
Серіалізація і парсинг |
Не має нативної підтримки для серіалізації чи парсингу.
(Проте можна збудувати власну підтримку для серіалізації та розбирання об'єктів |
Нативна підтримка серіалізації з
Нативна підтримка для парсингу JSON у |
Призначення властивостей об'єкта
Призначення властивостей об'єкта так само працює і для об'єктів типу Map, і це може спричинити суттєву плутанину.
Отже, може скластися враження, що це працює таким чином:
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
Однак цей спосіб присвоєння властивості ніяк не взаємодіє зі структурою даних Map
. Він лише використовує особливість звичайного об'єкта. Значення 'bla' не збереглося всередині Map
для запитів. Інші операції на даних зазнають невдачі:
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
Правильно використовувати Map
для зберігання даних через метод set(key, value)
.
const contacts = new Map();
contacts.set("Яся", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Яся"); // true
contacts.get("Галина"); // undefined
contacts.set("Галина", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Яся"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Роман"); // false
contacts.delete("Яся"); // true
console.log(contacts.size); // 1
Map-подібні браузерні API
Браузерні Map
-подібні об'єкти – це інтерфейси Web API, які багато в чому поводяться подібно до Map
.
Як і Map
, їхні записи можуть бути ітеровані в тому самому порядку, в якому були додані до об'єкта.
Також Map
-подібні об'єкти та Map
мають властивості та методи, що поділяють однакові назву та поведінку.
Проте, на відміну від Map
, ці об'єкти дозволяють лише певні попередньо визначені типи для ключів та значень кожного запису.
Дозволені типи задані у визначенні специфікації IDL.
Наприклад, RTCStatsReport
– це Map
-подібний об'єкт, який повинен використовувати рядки як ключі та об'єкти як значення.
Це визначено в специфікації IDL нижче:
interface RTCStatsReport {
readonly maplike<DOMString, object>;
};
Map
-подібні об'єкти – це або лише для читання, або для читання та запису (див. ключове слово readonly
у специфікації IDL вище).
Map
-подібні об'єкти лише для читання мають властивістьsize
, а також методи:entries()
,forEach()
,get()
,has()
,keys()
,values()
таSymbol.iterator
.- Записувані
Map
-подібні об'єкти, на додачу, мають методи:clear()
,delete()
таset()
.
Ці методи та властивості мають таку ж поведінку, як рівносильні сутності в Map
, окрім обмеження на типи ключів та значень.
Нижче – приклади Map
-подібних браузерних об'єктів лише для читання:
Конструктор
Map()
Створює новий об'єкт
Map
.
Статичні властивості
Map[Symbol.species]
Функція конструктора, що застосовується для створення похідних об'єктів.
Статичні методи
Map.groupBy()
Групує елементи переданого ітерованого об'єкта за допомогою значень, повернених переданою функцією зворотного виклику. Результівний об'єкт
Map
використовує унікальні значення з перевіркової функції як ключі, котрі можуть вживатися для отримання масиву елементів кожної групи.
Властивості примірника
Ці властивості означені на Map.prototype
і є спільними для всіх примірників Map
.
Map.prototype.constructor
Функція-конструктор, що створила об'єкт-примірник. Для примірників
Map
початковим значенням є конструкторMap
.Map.prototype[Symbol.toStringTag]
Початкове значення властивості
Symbol.toStringTag
– рядок"Map"
. Ця властивість використовується вObject.prototype.toString()
.Map.prototype.size
Повертає кількість пар ключ-значення об'єкта
Map
.Map.prototype[Symbol.toStringTag]
Початковим значенням властивості
Symbol.toStringTag
є рядок"Map"
. Вона використовується вObject.prototype.toString()
.
Методи примірника
Map.prototype.clear()
Видаляє всі пари ключ-значення з об'єкта
Map
.Map.prototype.delete()
Повертає
true
, якщо вказаний елемент знаходився всерединіMap
і був успішно видалений, або жfalse
, якщо вказаний елемент не існує. Після цьогоmap.has(key)
повертатимеfalse
.Map.prototype.entries()
Повертає новий об'єкт Iterator, що містить масив із двох елементів:
[key, value]
, на кожний елемент об'єктаMap
, у порядку їх вставки.Map.prototype.forEach()
Викликає функцію
callbackFn
один раз для кожної пари ключ-значення, наявної в об'єктіMap
, в порядку вставки. Якщо в методforEach
передано параметрthisArg
, його буде використано як значенняthis
для кожної функції зворотного виклику.Map.prototype.get()
Повертає значення, пов'язане з
key
, або жundefined
, якщо такого значення немає.Map.prototype.has()
Повертає булеве значення, яке відображає наявність чи відсутність якогось значення, пов'язаного з переданим ключем, в об'єкті
Map
.Map.prototype.keys()
Повертає новий об'єкт Iterator, що містить ключі кожного елементу об'єкта
Map
, в порядку їх вставки.Map.prototype.set()
Призначає значення за переданим ключем в об'єкті
Map
. Повертає об'єктMap
.Map.prototype.values()
Повертає новий об'єкт Iterator, який містить значення кожного елементу об'єкта
Map
, в порядку їх вставки.Map.prototype[Symbol.iterator]()
Повертає новий об'єкт Iterator, що містить масив із двох елементів:
[key, value]
, на кожний елемент об'єктаMap
, у порядку їх вставки.
Приклади
Застосування об'єкта Map
const myMap = new Map();
const keyString = "рядок";
const keyObj = {};
const keyFunc = function () {};
// встановлення значень
myMap.set(keyString, "значення, асоційоване з рядком");
myMap.set(keyObj, "значення, асоційоване з об'єктом");
myMap.set(keyFunc, "значення, асоційоване з функцією");
console.log(myMap.size); // 3
// отримання значень
console.log(myMap.get(keyString)); // "значення, асоційоване з рядком"
console.log(myMap.get(keyObj)); // "значення, асоційоване з об\'єктом"
console.log(myMap.get(keyFunc)); // "значення, асоційоване з функцією"
console.log(myMap.get("рядок")); // "значення, асоційоване з рядком"
// оскільки keyString === 'a string'
console.log(myMap.get({})); // undefined, оскільки keyObj !== {}
console.log(myMap.get(function () {})); // undefined, оскільки keyFunc !== function () {}
Застосування NaN як ключів для Map
Значення NaN
також можна використовувати як ключ. І хоча кожне значення NaN
не дорівнює саме собі (вираз NaN !== NaN
— істинний), наступний приклад спрацює, тому що значення NaN
неможливо розрізнити одне від одного:
const myMap = new Map();
myMap.set(NaN, "не число");
myMap.get(NaN);
// "не число"
const otherNaN = Number("foo");
myMap.get(otherNaN);
// "не число"
Перебирання об'єктів Map
за допомогою for...of
Об'єкти Map
можна перебирати за допомогою циклу for...of
:
const myMap = new Map();
myMap.set(0, "нуль");
myMap.set(1, "один");
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = нуль
// 1 = один
for (const key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// нуль
// один
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = нуль
// 1 = один
Перебирання об'єктів Map
за допомогою forEach()
Об'єкти Map
можна перебирати за допомогою методу forEach()
:
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = нуль
// 1 = один
Зв'язок з масивами
const kvArray = [
["key1", "value1"],
["key2", "value2"],
];
// Використаємо звичайний конструктор Map, щоб перетворити двовимірний масив комбінацій ключ-значення на `Map`
const myMap = new Map(kvArray);
console.log(myMap.get("key1")); // повертає "value1"
// Використаємо Array.from(), щоб перетворити об'єкт `Map` у двовимірний масив пар ключ-значення
console.log(Array.from(myMap)); // Покаже точнісінько такий самий масив, як kvArray
// Лаконічний спосіб зробити те саме, за допомогою оператора розгортання
console.log([...myMap]);
// Або ж використовуйте ітератори keys() чи values() і перетворюйте їх у масив
console.log(Array.from(myMap.keys())); // ["key1", "key2"]
Клонування і злиття об'єктів Map
Так само як і масиви, об'єкти Map
можна клонувати:
const original = new Map([[1, "one"]]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false (корисно для поверхневого порівняння)
[!NOTE] Майте на увазі, що власне дані тут клоновано не було.
Об'єкти Map
можна поєднувати, зберігаючи унікальність ключів:
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Зливаємо докупи два об'єкти `Map`. У разі конфлікту ключів наступний перезапише попередній.
// Синтаксис розгортання фактично перетворює `Map` на масив
const merged = new Map([...first, ...second]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Об'єкти Map
можна також об'єднувати з іншими масивами:
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Зливаємо докупи об'єкти `Map` з масивом. У разі конфлікту ключів наступний перезапише попередній.
const merged = new Map([...first, ...second, [1, "un"]]);
console.log(merged.get(1)); // un
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Map
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
@@iterator
|
Chrome Full support 43 | Edge Full support 12 | Firefox Full support 36 | Internet Explorer No support Ні | Opera Full support 30 | Safari Full support 10 | WebView Android Full support 43 | Chrome Android Full support 43 | Firefox for Android Full support 36 | Opera Android Full support 30 | Safari on iOS Full support 10 | Samsung Internet Full support 4.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
@@species
|
Chrome Full support 51 | Edge Full support 13 | Firefox Full support 41 | Internet Explorer No support Ні | Opera Full support 38 | Safari Full support 10 | WebView Android Full support 51 | Chrome Android Full support 51 | Firefox for Android Full support 41 | Opera Android Full support 41 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.5.0 |
@@toStringTag
|
Chrome Full support 44 | Edge Full support 79 | Firefox Full support 51 | Internet Explorer No support Ні | Opera No support Ні | Safari Full support 9.1 | WebView Android Full support 44 | Chrome Android Full support 44 | Firefox for Android Full support 51 | Opera Android No support Ні | Safari on iOS Full support 9.3 | Samsung Internet Full support 4.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
Map() constructor
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
new Map(iterable)
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
Map() without new throws
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 42 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 42 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
new Map(null)
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 37 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 37 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
clear
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 19 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 19 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
delete
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
entries
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 20 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 20 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
forEach
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 25 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 25 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
get
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
has
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
Key equality for -0 and 0
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 29 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 29 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
keys
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 20 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 20 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
set
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Partial support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
size
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 19 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 19 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
values
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 20 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 20 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |