Что такое WeakMap и WeakSet в JavaScript?

MiddleJavaScript · Frontend·Обновлено 13 июля 2026
Коротко
WeakMap и WeakSet — это коллекции, в которых ключи (WeakMap) или значения (WeakSet) могут быть только объектами и хранятся по слабой ссылке, что позволяет сборщику мусора удалять их автоматически, когда других ссылок не остаётся.

WeakMap

WeakMap — это коллекция пар «ключ → значение», где ключами могут быть только объекты (или зарегистрированные символы). Главное отличие от обычного Map — слабые ссылки на ключи.

Когда объект, используемый как ключ, больше не имеет других ссылок в программе, сборщик мусора вправе удалить его вместе с соответствующей записью в WeakMap. Это происходит автоматически, без явного вызова delete.

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

  • Нельзя перебрать ключи, значения или записи (forEach, keys(), values() отсутствуют).
  • Нет свойства size.
  • Ключами могут быть только объекты (или Symbol, зарегистрированный через Symbol.for).

Типичные сценарии применения WeakMap

Хранение приватных данных объектов:

const privateData = new WeakMap<object, { secret: string }>();

class User {
  constructor(secret: string) {
    // Приватные данные хранятся вне класса
    privateData.set(this, { secret });
  }

  getSecret(): string {
    return privateData.get(this)!.secret;
  }
}

const user = new User('my-password');
console.log(user.getSecret()); // 'my-password'
// После уничтожения user запись в privateData будет автоматически удалена

Кэширование результатов для DOM-элементов или объектов без утечек памяти:

const cache = new WeakMap<HTMLElement, string>();

function getLabel(el: HTMLElement): string {
  if (!cache.has(el)) {
    cache.set(el, el.getAttribute('aria-label') ?? '');
  }
  return cache.get(el)!;
}
// Когда el будет удалён из DOM и потеряет ссылки — кэш очистится сам

WeakSet

WeakSet — это коллекция уникальных объектов со слабыми ссылками. Аналог Set, но:

  • Хранит только объекты.
  • Не поддерживает итерацию и не имеет size.
  • Объекты автоматически удаляются при сборке мусора.

Типичный сценарий — отслеживание «посещённых» объектов:

const visited = new WeakSet<object>();

function processOnce(obj: object): void {
  if (visited.has(obj)) {
    console.log('Уже обработан');
    return;
  }
  visited.add(obj);
  // Обработка...
  console.log('Обработан впервые');
}

WeakRef и FinalizationRegistry (ES2021)

С ES2021 появились WeakRef для явных слабых ссылок и FinalizationRegistry для callback-ов при сборке мусора — они дополняют WeakMap/WeakSet в сложных сценариях управления памятью.

Map vs WeakMap — когда что выбирать

Критерий Map WeakMap
Ключи любые только объекты
Итерация есть нет
Размер (size) есть нет
Утечки памяти возможны исключены
Сценарий общие данные метаданные объектов

Что хочет услышать интервьюер

Понимание слабых ссылок и их связи со сборкой мусора — почему записи исчезают автоматически

Знание ограничений: ключи только объекты, нет итерации, нет size

Практические сценарии: хранение приватных данных, кэши без утечек, отслеживание объектов

Сравнение с Map/Set и понимание, когда использовать Weak-варианты

Упоминание риска утечек памяти с обычным Map при хранении метаданных DOM-элементов или сторонних объектов

Пример: WeakMap для приватных данных класса

const _private = new WeakMap<object, { count: number }>();

class Counter {
  constructor() {
    // Приватное состояние хранится вне экземпляра
    _private.set(this, { count: 0 });
  }

  increment(): void {
    _private.get(this)!.count++;
  }

  getCount(): number {
    return _private.get(this)!.count;
  }
}

const c = new Counter();
c.increment();
c.increment();
console.log(c.getCount()); // 2
// Когда c выходит из области видимости, запись в _private удаляется GC

Пример: WeakSet для защиты от повторной обработки

const processing = new WeakSet<object>();

async function handleRequest(req: object): Promise<void> {
  if (processing.has(req)) {
    throw new Error('Запрос уже обрабатывается');
  }

  processing.add(req);
  try {
    // Асинхронная обработка
    await new Promise(resolve => setTimeout(resolve, 100));
    console.log('Готово');
  } finally {
    // Явно убираем флаг после завершения
    processing.delete(req);
  }
}

Пример: Сравнение Map и WeakMap — утечка vs автоочистка

// Утечка памяти с обычным Map
const cache = new Map<HTMLElement, string>();

function registerWithMap(el: HTMLElement): void {
  cache.set(el, el.textContent ?? '');
  // Даже после удаления el из DOM, Map держит сильную ссылку
  // => el никогда не будет собран GC
}

// Безопасный вариант с WeakMap
const safeCache = new WeakMap<HTMLElement, string>();

function registerWithWeakMap(el: HTMLElement): void {
  safeCache.set(el, el.textContent ?? '');
  // После удаления el из DOM и потери всех ссылок
  // => GC удалит el и запись в safeCache автоматически
}

Типичные ошибки

Думают, что WeakMap можно итерировать или получить размер через .size

Используют примитивы (строки, числа) как ключи WeakMap, получая TypeError

Путают слабую ссылку с обычным удалением: не понимают, что запись исчезает только когда нет других ссылок на ключ

Не знают практических применений и воспринимают WeakMap/WeakSet как экзотику без реальной пользы

Считают поведение сборщика мусора детерминированным: ожидают немедленного удаления после потери ссылки

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее