Map и Set в JavaScript: когда не хватает объекта

Содержание

О проверке кода. Примеры не запускались — в отличие от материалов по Node.js с фактическим выводом. Говорим прямо, а не ставим бейдж «проверено».

Map и Set появились в ECMAScript 6 и закрыли задачи, для которых объект и массив подходят плохо. Разберём, когда они действительно лучше.

Map: словарь без ограничений объекта

const map = new Map();

map.set('name', 'Аня');       // строковый ключ
map.set(42, 'ответ');         // числовой ключ
const userKey = { id: 1 };
map.set(userKey, 'данные');   // ОБЪЕКТ как ключ — объект так не умеет!

map.get('name');              // 'Аня'
map.has(42);                  // true
map.size;                     // 3
map.delete('name');

Ключом Map может быть что угодно — объект, число, функция, не только строка. У объекта ключи всегда приводятся к строке (obj[42] и obj['42'] — одно и то же), а Map различает типы. Плюс у Map есть size (у объекта надо считать Object.keys().length), он хранит порядок вставки и не тащит унаследованные из прототипа свойства.

Объект против Map

Обычный объект

Фиксированная структура

  • Ключи только строки/символы
  • Object.keys().length для размера
  • Наследует из прототипа
  • Удобен для записей данных, JSON

Map

Динамический словарь

  • Ключ ЛЮБОГО типа, включая объекты
  • .size из коробки
  • Гарантированный порядок вставки
  • Быстрые add/delete, удобный перебор

Перебор Map

const map = new Map([['a', 1], ['b', 2]]);   // можно создать из массива пар

for (const [key, value] of map) {
  console.log(key, value);   // 'a' 1, потом 'b' 2 — в порядке вставки
}

[...map.keys()];    // ['a', 'b']
[...map.values()];  // [1, 2]
map.forEach((v, k) => console.log(k, v));

Map итерируется парами [ключ, значение] через for...of — с деструктуризацией это читается естественно. Создать Map можно сразу из массива пар, а превратить обратно в объект — через Object.fromEntries(map).

Set: только уникальные значения

const set = new Set();

set.add(1);
set.add(2);
set.add(1);       // дубликат — молча проигнорирован

set.size;         // 2 — не 3!
set.has(1);       // true — быстрая проверка принадлежности
set.delete(2);

Set хранит только уникальные значения — повторные add того же значения ничего не меняют. Главные применения: убрать дубликаты и быстро проверять «есть ли элемент» через has (быстрее, чем array.includes на больших объёмах).

Убрать дубликаты из массива

const arr = [1, 2, 2, 3, 3, 3];

[...new Set(arr)];   // [1, 2, 3] — уникальные

// уникальные слова
const words = 'кот пёс кот рыба пёс'.split(' ');
[...new Set(words)]; // ['кот', 'пёс', 'рыба']

[...new Set(arr)] — самый короткий способ убрать повторы из массива. Set отбрасывает дубли, spread разворачивает обратно в массив. Одна строка вместо цикла с проверками — идиома, которую стоит запомнить.

Быстрая проверка принадлежности

// ❌ на большом массиве includes медленный (перебор)
const banned = ['spam', 'bot', 'ad'];
banned.includes(word);          // проверяет по очереди

// ✅ Set проверяет за постоянное время
const bannedSet = new Set(['spam', 'bot', 'ad']);
bannedSet.has(word);            // мгновенно, независимо от размера

Когда нужно многократно проверять принадлежность к большому набору (чёрный список, посещённые узлы, теги), Set.has работает за постоянное время, а array.includes — за линейное (перебирает всё). Для одной проверки разница незаметна, для тысяч — существенна.

Что выбрать

  • Объект — структура известна заранее, ключи-строки, нужен JSON или запись данных ({ name, age });
  • Map — ключи динамические или не строки, часто добавляют/удаляют, нужен size или порядок (кэш, счётчики по объектам);
  • Массив — упорядоченный список, где важны индексы и порядок, возможны дубли;
  • Set — нужна только уникальность и быстрая проверка наличия, порядок и индексы не важны.

Правило то же, что и везде: структура под задачу. Не заменяйте объект на Map без причины — для простых записей объект привычнее и сериализуется в JSON.


Смежные темы: объекты — когда достаточно их; массивы — упорядоченные данные; spread и rest — трюк с уникальностью. Полный список — в уроках JavaScript.

Частые вопросы

Чем Map лучше обычного объекта?
У Map ключом может быть что угодно (объект, число, функция), а не только строка. Он хранит порядок вставки, имеет свойство size, легко перебирается и не наследует лишних свойств из прототипа. Для словаря данных Map чище объекта.
Что делает Set?
Set хранит только уникальные значения — дубликаты автоматически отбрасываются. Это удобно, чтобы убрать повторы из массива ([…new Set(arr)]) или быстро проверять принадлежность через has за постоянное время.
Как перебрать Map?
Через for…of: for (const [key, value] of map). Map итерируется парами в порядке вставки. Также есть map.keys(), map.values() и map.entries(). Метод forEach тоже поддерживается.
Когда брать объект, а когда Map?
Объект — когда структура фиксирована и ключи известны заранее (запись данных). Map — когда ключи динамические, часто добавляют и удаляют, нужен размер, порядок или ключи не строки. Для конфигов объект, для кэша Map.
Как убрать дубликаты из массива?
Обернуть массив в Set и развернуть обратно: […new Set(arr)]. Set отбросит повторы, а spread вернёт массив. Это самый короткий способ получить уникальные значения.