Что такое WeakMap и WeakSet в JavaScript?
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 как экзотику без реальной пользы
Считают поведение сборщика мусора детерминированным: ожидают немедленного удаления после потери ссылки


