Map проти об'єкта
Багато хто вважає, що Map і звичайний об'єкт {} це майже одне й те саме, але між ними є суттєві відмінності. Object це структура даних загального призначення для зберігання властивостей, ключі яких є рядками або символами, а Map це спеціалізована колекція для зберігання пар «ключ → значення» з ключами будь-якого типу і збереженням порядку вставки.
Теорія
TL;DR
- Ключі об'єкта, це тільки рядки та символи, ключі
Map, це будь-які значення. Mapзавжди повертає елементи в порядку вставки, об'єкт сортує цілочислові ключі.- Розмір:
Object.keys(obj).lengthпроти простогоmap.size. Mapітерабельний напряму черезfor...of, об'єкт, ні.Mapшвидший при частих вставках і видаленнях.Mapне успадковуєObject.prototype, тож немає конфліктів ізtoStringчиhasOwnProperty.- Конвертація в обидва боки:
new Map(Object.entries(obj))іObject.fromEntries(map).
Швидкий приклад
const obj = {};
const map = new Map();
const keyObj = { id: 1 };
obj[keyObj] = 'object value';
map.set(keyObj, 'map value');
console.log(obj); // { "[object Object]": "object value" }
console.log(map.get(keyObj)); // "map value"В об'єкті ключ перетворився на рядок "[object Object]", а в Map залишився справжнім об'єктом. Якби ми додали другий об'єкт як ключ, в obj він би просто перезаписав перший запис, бо рядок вийшов би той самий.
Тип ключів
| Особливість | Object | Map |
|---|---|---|
| Ключі | Тільки рядки або символи | Будь-які типи (рядки, числа, об'єкти, функції тощо) |
Це найважливіша практична різниця: лише Map дозволяє використати об'єкт або функцію як ключ і таким чином «прив'язати» дані до конкретного екземпляра.
Порядок, розмір і перебір
Порядок ключів.
| Особливість | Object | Map |
|---|---|---|
| Порядок ключів | Частково гарантований (ES6 і новіші) | Завжди зберігається |
Map завжди повертає елементи в порядку вставки, а об'єкт поводиться інакше з числовими ключами:
const obj = { 2: 'b', 1: 'a' };
console.log(Object.keys(obj)); // ["1", "2"] (числа сортуються за зростанням)const map = new Map([[2, 'b'], [1, 'a']]);
console.log([...map.keys()]); // [2, 1] (порядок зберігається)Розмір.
| Особливість | Object | Map |
|---|---|---|
| Розмір | Object.keys(obj).length | map.size |
const map = new Map([['a', 1], ['b', 2]]);
console.log(map.size); // 2Різниця не лише в зручності: Object.keys() створює проміжний масив, а size це просто читання лічильника.
Ітерабельність.
| Особливість | Object | Map |
|---|---|---|
| Перебирається напряму | Ні | Так |
const map = new Map([['x', 10], ['y', 20]]);
for (const [key, value] of map) {
console.log(key, value);
}
// x 10
// y 20Для об'єкта доведеться викликати Object.keys(), Object.entries() і подібні методи.
Продуктивність і прототип
| Особливість | Object | Map |
|---|---|---|
| Часті вставки та видалення | Повільніше | Швидше |
| Ітерації по елементах | Незручно | Зручно через for...of |
Map оптимізований саме під часті додавання, видалення і обхід, тоді як рушій оптимізує об'єкти під стабільну «форму» з відомим набором полів.
| Особливість | Object | Map |
|---|---|---|
Успадковує від Object.prototype | Так | Ні |
Може конфліктувати з методами (toString, hasOwnProperty) | Так | Ні |
У Map немає «сміття» у вигляді вбудованих методів, тому будь-який ключ можна використовувати безпечно. В об'єкті ключ "hasOwnProperty" або "__proto__" здатен зламати логіку перевірок.
Перетворення між ними
const obj = { a: 1, b: 2 };
const map = new Map(Object.entries(obj));
console.log(map); // Map(2) { 'a' => 1, 'b' => 2 }
const backToObj = Object.fromEntries(map);
console.log(backToObj); // { a: 1, b: 2 }Object.entries() і Object.fromEntries() дозволяють легко конвертувати туди й назад. Це ж і спосіб серіалізувати Map, бо напряму JSON.stringify(map) дає {}.
Що і коли використовувати
| Використовуй | Коли |
|---|---|
Object | структура даних із фіксованими властивостями (наприклад, модель користувача) |
Map | динамічні ключі, ключі будь-якого типу, кеш, часті операції додавання та видалення |
Коротко:
| Особливість | Object | Map |
|---|---|---|
| Тип ключів | рядок або символ | будь-який тип |
| Порядок ключів | частково впорядкований | зберігається |
| Розмір | треба рахувати вручну | .size |
| Ітерабельність | через Object.keys() | напряму (for...of) |
| Продуктивність | нижча | вища |
| Застосування | статичні дані | динамічні колекції |
Типові помилки
- Використовувати об'єкт як ключ звичайного об'єкта. Ключ мовчки стане рядком
"[object Object]", і всі об'єкти зіллються в один запис. - Покладатися на порядок числових ключів об'єкта. Цілочислові ключі завжди сортуються за зростанням, незалежно від порядку вставки.
- Писати
map.lengthабоmap['key']. Перше цеundefined, друге створює властивість поза колекцією; правильно,map.sizeіmap.get('key'). - Серіалізувати
Mapнапряму.JSON.stringify(map)поверне{}, спочатку потрібенObject.fromEntries(map)або масив із[...map]. - Брати
Mapскрізь «бо швидше». Для невеликої фіксованої моделі об'єкт зручніший: деструктуризація, скорочений запис, підтримка в JSON. - Забувати, що
Mapтримає ключі сильно. Об'єкти-ключі не зникнуть із пам'яті, поки живий самMap, для слабких посилань єWeakMap.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.