Map и Object ― не просто словари
Вы наверняка используете объекты (Object) для хранения данных в формате «ключ-значение». Это основа основ в JavaScript. Например:
const user = {
name: «Андрей»,
age: 30,
isAdmin: false
};
console.log(user.name); // «Андрей»
Это удобно, но у объектов есть ограничение: их ключи могут быть только строками или специальным типом Symbol. Если вы попытаетесь использовать что-то другое (например, другой объект) в качестве ключа, JavaScript неявно преобразует его в строку (часто в «[object Object]»), что может привести к неожиданным коллизиям и потере данных.
И тут на помощь приходит Map. Map — это тоже коллекция пар «ключ-значение», но с важным отличием. Ключом в Map может быть значение абсолютно любого типа – строка, число, булево значение, null, undefined, Symbol, и даже другой объект или функция.
Ключевые отличия Map от Object:
- Map — любой тип, Object — только string или Symbol.
- Map гарантированно сохраняет порядок добавления элементов и перебирает их именно в этом порядке. У обычных объектов до недавнего времени порядок не был гарантирован. Хотя современные движки часто сохраняют его для строковых ключей, полагаться на это не стоит.
- У Map есть простое свойство size для получения количества элементов. У объекта для этого нужно получать массив ключей (Object.keys(obj).length).
- Map напрямую итерируется (например, через for...of), что удобнее, чем перебор ключей объекта (Object.keys, Object.entries).
- Производительность ― для сценариев с частым добавлением и удалением пар ключ-значение Map часто оказывается производительнее.
Представьте Object как простой блокнот, где вы пишете имя (строка) и рядом номер телефона. А Map – это продвинутая картотека, где карточкой-ключом может быть не только имя, но и фотография человека, его отпечаток пальца (другой тип данных), а к этой карточке привязана информация (значение).
Set и массивы ― коллекции без дубликатов
Массивы (Array) – это упорядоченные коллекции элементов. Вы можете хранить в них что угодно, и элементы могут повторяться:
const userRoles = [“admin”, “editor”, “viewer”, “editor”]; // «editor» повторяется
console.log(userRoles[1]); // «editor»
console.log(userRoles.length); // 4
Массивы отлично подходят для хранения списков, где важен порядок и допустимы дубликаты. А что, если нам нужна коллекция, где каждый элемент уникален? Например, список ID пользователей, которые посетили страницу, — нам не нужно хранить дубликаты, если пользователь обновил страницу 5 раз. Для этого идеально подходит Set — это коллекция, которая хранит только уникальные значения любого типа. Если вы попытаетесь добавить в Set элемент, который там уже есть, ничего не произойдет.
Ключевые отличия Set от Array:
- Set гарантирует уникальность элементов. Array позволяет дубликаты.
- В Set проверка наличия элемента (mySet.has(element)) работает очень быстро, особенно на больших коллекциях, в отличие от myArray.includes(element) или myArray.indexOf(element) !== -1, которые могут быть медленнее.
- Set используется в первую очередь для хранения уникальных коллекций и быстрой проверки принадлежности к этой коллекции. Array — для упорядоченных списков и операций, связанных с индексами.
- У элементов Set нет числовых индексов, как у массива. Доступ к элементам происходит через итерацию или проверку наличия.
Приведем аналогию. Array — это список покупок, где «молоко» может быть упомянуто два раза, если вам нужно две упаковки. Set — что-то вроде списка приглашенных на вечеринку гостей. Неважно, сколько раз вы вписывали имя друга: в итоговом списке он будет только один раз.
Применение Map
Map — это коллекция ключ-значение, но ее настоящая сила раскрывается там, где обычный объект (Object) пасует. Давайте рассмотрим конкретные ситуации, где Map не просто удобен, а является наилучшим решением.
Представьте, что вы строите интерактивную веб-страницу, и вам нужно хранить дополнительную информацию, связанную с конкретными элементами на странице (DOM-узлами). Если вы попытаетесь использовать DOM-узел как ключ объекта, он будет неявно приведен к строке ([object HTMLDivElement] или что-то подобное). Если у вас несколько разных div-элементов, они все превратятся в один и тот же ключ в объекте. Данные для одного элемента будут перезаписывать данные для другого.
В свою очередь Map примет сам DOM-элемент в качестве уникального ключа.
const element1 = document.getElementById('button1');
const element2 = document.getElementById('tooltip1');
const elementDataMap = new Map();
// Используем сами DOM-элементы как ключи
elementDataMap.set(element1, { clicks: 0, text: "Нажми меня" });
elementDataMap.set(element2, { isVisible: false, position: "top" });
console.log(elementDataMap.size); // 2 - все верно!
// Получаем данные по конкретному элементу
const buttonData = elementDataMap.get(element1);
if (buttonData) {
buttonData.clicks++;
console.log(`Кнопка нажата ${buttonData.clicks} раз.`);
}
const tooltipData = elementDataMap.get(element2);
console.log(tooltipData); // { isVisible: false, position: 'top' }
// Проверка наличия
if (elementDataMap.has(element1)) {
console.log("Данные для кнопки есть.");
}
// Удаление
// elementDataMap.delete(element2);
Точно так же ключами могут быть любые другие объекты (например, экземпляры ваших классов), функции, NaN. Map сохраняет их идентичность.
Узнать, сколько элементов в Map, элементарно — myMap.size. С объектом нужно вызывать Object.keys(myObj).length, что менее прямолинейно и потенциально менее эффективно.
- Map создан для итерации. Цикл for...of напрямую работает с парами [key, value]. Также есть удобные методы .keys(), .values(), .entries(), возвращающие итераторы.
- Производительность. Хотя для простых случаев разница может быть невелика, в сценариях, где вы постоянно добавляете и удаляете большое количество пар ключ-значение, Map может показывать лучшую производительность, чем манипуляции с ключами объекта.
Применение Set
Set приходит на помощь, когда вам нужна коллекция, но с одним важным условием: никаких дубликатов. А еще — когда нужно быстро проверить, есть ли уже такой элемент в коллекции, или нет.
У вас есть массив, полученный из разных источников, и в нем могут быть повторы. Вам нужен список уникальных значений. Массивы спокойно хранят дубликаты. Чтобы получить уникальные значения, нужно писать дополнительный код (фильтрация, использование indexOf в цикле), что может быть громоздко и не всегда эффективно. Set делает это за вас автоматически и очень элегантно.
//Массив строк (тегов)
const userInputTags = ["js", "css", "html", "js", "react", "css", "js"];
const uniqueTags = [...new Set(userInputTags)]; // Сразу создаем Set и конвертируем в массив
console.log(uniqueTags); // [ 'js', 'css', 'html', 'react' ]
Вы просто передаете массив в конструктор new Set(), и он сам позаботится об удалении дубликатов.
Представьте, что у вас есть большой список разрешенных ID пользователей, или список заблокированных IP-адресов. Вам нужно постоянно проверять, принадлежит ли входящий ID/IP к этому списку. Метод set.has(value) оптимизирован для быстрой проверки наличия. Он работает значительно быстрее, чем поиск в массиве, особенно на больших объемах данных.
// Предположим, этот список может быть очень большим
const adminUserIDs = new Set([5, 12, 42, 101, 555, 1024]);
function checkAdminAccess(userId) {
console.time(`Проверка доступа для ${userId}`); // Замеряем время
const isAdmin = adminUserIDs.has(userId); // Используем быстрый .has()
console.timeEnd(`Проверка доступа для ${userId}`); // Заканчиваем замер
if (isAdmin) {
console.log(`Пользователь ${userId} - администратор.`);
} else {
console.log(`Пользователь ${userId} - обычный пользователь.`);
}
}
checkAdminAccess(42); // Быстро найдет
checkAdminAccess(100); // Быстро определит, что такого нет
Хотя на маленьких наборах разница незаметна, для тысяч и миллионов записей Set будет на порядки быстрее.
Отличия Map от Set
Главное отличие запомнить просто: Map хранит пары ключ ― значение. Он используется для ассоциаций, когда вам нужно связать одно значение с другим. Вы ищете и работаете со значениями по их ключам.
Set хранит только значения (уникальные). Он используется для коллекций уникальных элементов, когда вам важен сам факт наличия или отсутствия элемента в наборе.
Хотите «словарь», где ключ может быть чем угодно? Используйте Map. Хотите хранить список уникальных ID, имен, объектов без дубликатов? Используйте Set.
Map и Set — это специализированные структуры данных, которые делают код для определенных задач более читаемым, предсказуемым и часто более производительным. Не бойтесь использовать их там, где они подходят лучше, чем стандартные Object и Array.