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), он хранит порядок вставки и не тащит унаследованные из прототипа свойства.
Обычный объект
Фиксированная структура
- Ключи только строки/символы
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.