Что такое Map в JavaScript?

Ответ

Map — это встроенная в JavaScript коллекция для хранения пар ключ-значение, где ключом может быть значение любого типа (объект, функция, примитив), в отличие от обычных объектов, где ключи автоматически преобразуются в строки.

Зачем использовать Map в Node.js?

  • Ключи любого типа: Идеально для сопоставления объектов метаданных или DOM-элементов (в SSR) со значениями.
  • Сохранение порядка вставки: При итерации элементы возвращаются в порядке их добавления.
  • Удобный API: Имеет методы get, set, has, delete и свойство size.
  • Производительность: В сценариях частого добавления/удаления пар Map может быть быстрее объекта.

Базовый пример:

// Создание и использование Map
const userSessions = new Map();

// Ключом может быть объект (например, WebSocket соединение)
const socket = { id: 'ws-123' };
const user = { id: 'user-456', name: 'Alice' };

userSessions.set(socket, user); // Ключ - объект
userSessions.set(user.id, socket); // Ключ - строка

console.log(userSessions.get(socket)); // { id: 'user-456', name: 'Alice' }
console.log(userSessions.has(user.id)); // true
console.log(userSessions.size); // 2

// Итерация (порядок сохранён)
for (const [key, value] of userSessions) {
  console.log(key, '->', value);
}
// Вывод:
// { id: 'ws-123' } -> { id: 'user-456', name: 'Alice' }
// 'user-456' -> { id: 'ws-123' }

// Удаление
userSessions.delete(user.id);

Практический пример в Node.js: простой in-memory кэш с TTL (Time To Live):

class SimpleCache {
  constructor() {
    this.cache = new Map(); // Хранит данные: ключ -> { value, expiry }
  }

  set(key, value, ttlMs = 5000) {
    const expiry = Date.now() + ttlMs;
    this.cache.set(key, { value, expiry });
    // Автоочистка просроченных записей (можно вынести в отдельный интервал)
    setTimeout(() => {
      if (this.cache.get(key)?.expiry < Date.now()) {
        this.cache.delete(key);
      }
    }, ttlMs);
  }

  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;
    if (item.expiry < Date.now()) {
      this.cache.delete(key);
      return null;
    }
    return item.value;
  }
}

// Использование
const cache = new SimpleCache();
cache.set('api_response_123', { data: 'someData' }, 10000);
console.log(cache.get('api_response_123')); // { data: 'someData' }
setTimeout(() => {
  console.log(cache.get('api_response_123')); // null (после истечения TTL)
}, 11000);
Map vs Object: Критерий Map Object
Ключи Любого типа Только String или Symbol
Порядок Сохраняет порядок вставки Не гарантирован (хотя в современных JS есть порядок для строковых ключей)
Размер map.size Object.keys(obj).length
Итерация Прямо итерируем (for...of) Требуется Object.keys, for...in
Прототип Не имеет унаследованных ключей Может иметь ключи из цепочки прототипов
Производительность Лучше при частых добавлениях/удалениях Лучше в сценариях статической структуры

Ответ 18+ 🔞

Слушай, я тут подумал — сколько же народу до сих пор тупо юзает обычные объекты для всего подряд, а про Map нихуя не знает или знает, но лень. Это ж, бля, просто ёперный театр какой-то!

Вот смотри, Map — это такая встроенная в JS штука, которая хранит пары «ключ — значение». И главный её прикол, от которого сам от себя охуеешь, — ключом может быть что угодно: объект, функция, хоть твой левый тапочек. Не то что у объектов, где все ключи автоматом в строку превращаются, хоть ты тресни.

Ну и нахуя он нужен в Node.js, спросишь?

  • Ключи любого типа: Идеально, когда надо привязать какие-то метаданны к объекту или, там, DOM-элементу (если на SSR городишь). Никаких костылей.
  • Порядок вставки помнит: Когда перебираешь, всё идёт ровно в том порядке, в каком ты это запихал. У обычного объекта с этим, бля, волнение ебать — как карта ляжет.
  • Удобный API: Есть нормальные методы get, set, has, delete и свойство size. Никаких этих Object.keys(obj).length — терпения ноль ебать на это.
  • Бывает быстрее: Если ты постоянно что-то добавляешь и удаляешь, Map может оказаться шустрее объекта. Хуй с горы, но факт.

Смотри, как просто:

// Создаём и юзаем Map
const userSessions = new Map();

// Ключом может быть объект (ну, типа, WebSocket-соединение)
const socket = { id: 'ws-123' };
const user = { id: 'user-456', name: 'Alice' };

userSessions.set(socket, user); // Ключ — объект, ёпта!
userSessions.set(user.id, socket); // А тут ключ — строка

console.log(userSessions.get(socket)); // { id: 'user-456', name: 'Alice' }
console.log(userSessions.has(user.id)); // true
console.log(userSessions.size); // 2

// Перебор (всё в порядке добавления)
for (const [key, value] of userSessions) {
  console.log(key, '->', value);
}
// Выведет:
// { id: 'ws-123' } -> { id: 'user-456', name: 'Alice' }
// 'user-456' -> { id: 'ws-123' }

// Удаление — раз плюнуть
userSessions.delete(user.id);

А вот практический пример: простой кэш в памяти с TTL (время жизни):

class SimpleCache {
  constructor() {
    this.cache = new Map(); // Храним: ключ -> { value, expiry }
  }

  set(key, value, ttlMs = 5000) {
    const expiry = Date.now() + ttlMs;
    this.cache.set(key, { value, expiry });
    // Можно поставить автоочистку просрочки (или в отдельный интервал вынести)
    setTimeout(() => {
      if (this.cache.get(key)?.expiry < Date.now()) {
        this.cache.delete(key);
      }
    }, ttlMs);
  }

  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;
    if (item.expiry < Date.now()) {
      this.cache.delete(key); // Просрочился — нахуй
      return null;
    }
    return item.value;
  }
}

// Используем
const cache = new SimpleCache();
cache.set('api_response_123', { data: 'someData' }, 10000);
console.log(cache.get('api_response_123')); // { data: 'someData' }
setTimeout(() => {
  console.log(cache.get('api_response_123')); // null (протухло)
}, 11000);

И главное — Map vs обычный Object, чтобы ты понимал, где что применять:

Критерий Map Object
Ключи Что угодно, хоть манда с ушами Только строка или символ
Порядок Чётко помнит, кто за кем зашёл Гарантий нет, хотя в современных движках для строковых ключей порядок есть
Размер map.size — раз, и готово Object.keys(obj).length — ещё посчитай
Итерация Прямо for...of Надо Object.keys или for...in
Прототип Чистый, унаследованного говна нет Может быть засран ключами из цепочки прототипов
Скорость Часто быстрее, когда ключей овердохуища и их постоянно меняют Быстрее, если структура статичная и известная

Короче, чувак, если тебе нужно гибко ассоциировать данные — бери Map и не еби мозг. А если структура простая и известная заранее — объект сойдёт. Главное — понимать разницу, а не тыкать наугад.