← Назад к вопросам

В чем разница между Map, Set, WeakMap, WeakSet?

2.3 Middle🔥 153 комментариев
#JavaScript Core

Комментарии (3)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Map, Set, WeakMap, WeakSet: полное сравнение

Эти четыре структуры данных часто путают. Давай разберёмся в различиях и когда использовать каждую.

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

Set — это коллекция уникальных значений любого типа:

const set = new Set();
set.add(1);
set.add('hello');
set.add(1); // Дубликат, не добавится

console.log(set.size); // 2
console.log(set.has(1)); // true

// Итерация
for (const value of set) {
  console.log(value);
}

Методы Set:

const set = new Set([1, 2, 3, 2, 1]);

// Проверка наличия
set.has(2); // true

// Добавление
set.add(4); // Set { 1, 2, 3, 4 }

// Удаление
set.delete(2); // true
set.delete(10); // false (элемента нет)

// Очистка
set.clear(); // Set {}

// Размер
set.size; // количество элементов

Map: ключ-значение пары

Map — это коллекция ключ-значение пар (как объект, но лучше):

const map = new Map();
map.set('name', 'John');
map.set('age', 30);
map.set(1, 'один'); // Ключ может быть любого типа!

console.log(map.get('name')); // 'John'
console.log(map.get(1)); // 'один'
console.log(map.size); // 3

Методы Map:

const map = new Map();

// Добавление
map.set('key', 'value');
map.set(42, 'number key');

// Получение
map.get('key'); // 'value'
map.get('unknown'); // undefined

// Проверка
map.has('key'); // true

// Удаление
map.delete('key'); // true

// Очистка
map.clear();

// Размер
map.size; // количество пар

// Итерация
for (const [key, value] of map) {
  console.log(key, value);
}

Map vs Object: преимущества

// Object
const obj = {};
obj['key'] = 'value';
obj[1] = 'number'; // Будет преобразовано в '1'

// Map
const map = new Map();
map.set('key', 'value');
map.set(1, 'number'); // Ключ остаётся числом!

console.log(Object.keys(obj)); // ['1', 'key'] - все строки
console.log([...map.keys()]); // [1, 'key'] - оригинальные типы

Почему Map лучше Object:

  1. Ключи любого типа: объект преобразует ключи в строки
  2. Итерация в порядке вставки: объект не гарантирует порядок
  3. Есть метод has(): в объекте нужно проверять undefined
  4. Есть размер (size): объект нужно считать через Object.keys()
// Object проблемы
const obj = { hasOwnProperty: 'value' };
obj.hasOwnProperty('name'); // TypeError! Поле перезаписало метод

// Map не имеет таких проблем
const map = new Map();
map.set('hasOwnProperty', 'value'); // OK!

WeakSet: "слабые" уникальные значения

WeakSet — это Set, но ключи могут быть удалены сборщиком мусора:

const weakSet = new WeakSet();

const obj = { name: 'John' };
weakSet.add(obj);

console.log(weakSet.has(obj)); // true
weakSet.delete(obj);
console.log(weakSet.has(obj)); // false

Ограничения WeakSet:

const weakSet = new WeakSet();

// МОЖНО добавлять
weakSet.add({});
weakSet.add(new Date());
weakSet.add(new Map());

// ЗАПРЕЩЕНО - примитивные типы
weakSet.add(1); // TypeError
weakSet.add('string'); // TypeError
weakSet.add(true); // TypeError
weakSet.add(null); // TypeError
weakSet.add(undefined); // TypeError

Ключевое отличие: автоматическая очистка:

const weakSet = new WeakSet();
let obj = { data: 'important' };

weakSet.add(obj);
console.log(weakSet.has(obj)); // true

obj = null; // Удалили последнюю ссылку на объект
// GC удалит объект и автоматически удалит из WeakSet!

// Нет способа проверить, есть ли в WeakSet (потому что он может быть GC'д)
weakSet.has(obj); // false (потому что obj === null теперь)

WeakMap: "слабые" ключ-значение пары

WeakMap — это Map, но ключи могут быть удалены сборщиком мусора:

const weakMap = new WeakMap();

const key1 = { id: 1 };
const key2 = { id: 2 };

weakMap.set(key1, 'value1');
weakMap.set(key2, 'value2');

console.log(weakMap.get(key1)); // 'value1'

Ограничения WeakMap:

const weakMap = new WeakMap();

// МОЖНО использовать как ключи только объекты
const obj = {};
weakMap.set(obj, 'value');

// ЗАПРЕЩЕНО - примитивные типы
weakMap.set('string', 'value'); // TypeError
weakMap.set(123, 'value'); // TypeError
weakMap.set(true, 'value'); // TypeError

WeakMap vs Map: автоматическая очистка памяти

// Map - никогда не удаляет элементы
const cache = new Map();
let user = { id: 1, name: 'John' };
cache.set(user, 'user data');

user = null; // Удалили ссылку
// НО объект остаётся в cache!
console.log(cache.size); // 1 (утечка памяти!)

// WeakMap - автоматически удаляет
const weakCache = new WeakMap();
let user2 = { id: 2, name: 'Jane' };
weakCache.set(user2, 'user data');

user2 = null; // Удалили ссылку
// Объект удалится из weakCache автоматически

Практический пример: кэширование с WeakMap

const cache = new WeakMap();

function getCachedResult(obj) {
  if (cache.has(obj)) {
    console.log('From cache');
    return cache.get(obj);
  }

  // Дорогостоящее вычисление
  const result = heavyCalculation(obj);
  cache.set(obj, result);
  return result;
}

let data = { value: 100 };
getCachedResult(data); // Вычисляет
getCachedResult(data); // From cache

data = null; // Удалили ссылку
// Кэш автоматически очищается (нет утечки памяти)

Сравнительная таблица

СвойствоSetMapWeakSetWeakMap
ХранитЗначенияКлюч-значениеЗначенияКлюч-значение
Типы элементовЛюбыеКлюч: любые, Значение: любыеТолько объектыКлюч: только объекты
ИтерируемыйДаДаНетНет
Имеет sizeДаДаНетНет
Удаление GCНетНетДаДа
ИспользованиеУникальные значенияКэш, словариПриватные данныеКэш объектов

Когда использовать

Set — когда нужны уникальные значения:

const uniqueIds = new Set([1, 2, 2, 3, 3, 3]);
console.log(uniqueIds.size); // 3

Map — вместо объекта, когда нужна гибкость:

const userMap = new Map();
const userId = { id: 123 }; // Объект как ключ
userMap.set(userId, 'John');

WeakSet — для приватных флагов объектов:

const processed = new WeakSet();

function processUser(user) {
  if (processed.has(user)) return;
  // обработка
  processed.add(user);
}

WeakMap — для кэширования связанных с объектами данных:

const domNodeMetadata = new WeakMap();
const node = document.getElementById('my-element');
domNodeMetadata.set(node, { clicks: 0 });

Итоговые правила

  1. Set для уникальности, Map для словарей
  2. WeakSet/WeakMap только для ключей-объектов и автоматической очистки
  3. Не итерируй WeakSet/WeakMap — это невозможно
  4. WeakMap идеален для кэширования (нет утечек памяти)
  5. Используй Map вместо Object для гибкости