WeakMap и WeakSet
WeakMapиWeakSet— коллекции, хранящие слабые ссылки на объекты-ключи: если объект больше не имеет других ссылок, он автоматически удаляется сборщиком мусора вместе с соответствующей записью.
Зачем нужно
Обычные Map и Set удерживают объекты в памяти, пока сама коллекция жива — это приводит к утечкам памяти при хранении данных, привязанных к DOM-элементам или временным объектам. WeakMap/WeakSet автоматически освобождают память при уничтожении объекта-ключа, не требуя ручной очистки.
Где используется
- Приватные данные объектов (до
#fields) - Кэш, привязанный к DOM-элементу (автоочищается при удалении из DOM)
- Метаданные объектов без удержания их в памяти
- Tracking обработанных объектов (посещённые вершины графа)
- Memoization с автоматической инвалидацией
Основной контент
WeakMap
// Ключи WeakMap — только объекты
const weakMap = new WeakMap();
let element = document.getElementById('btn');
weakMap.set(element, { clicks: 0, created: Date.now() });
// Методы: set, get, has, delete — нет size, нет итерации
console.log(weakMap.get(element)); // { clicks: 0, created: ... }
console.log(weakMap.has(element)); // true
// При удалении элемента из DOM и обнулении ссылки
// — GC автоматически удалит запись из WeakMap
element = null; // больше нет сильных ссылок
// weakMap запись будет удалена GC при следующей сборке
Приватные данные через WeakMap
// Паттерн: WeakMap для приватного состояния (до #fields)
const _private = new WeakMap();
class Person {
constructor(name, age) {
_private.set(this, { name, age, _secret: 'hidden' });
}
get name { return _private.get(this).name; }
get age { return _private.get(this).age; }
greet {
const { name, age } = _private.get(this);
return `Привет, я ${name}, мне ${age} лет`;
}
}
const p = new Person('Иван', 25);
console.log(p.name); // 'Иван'
console.log(p.greet); // 'Привет, я Иван, мне 25 лет'
console.log(p._secret); // undefined — приватное!
// При удалении p — _private запись автоматически очищается
WeakSet
// WeakSet — множество объектов со слабыми ссылками
const processedNodes = new WeakSet();
function processNode(node) {
if (processedNodes.has(node)) {
console.log('Уже обработан, пропускаем');
return;
}
// Обрабатываем...
processedNodes.add(node);
}
// Tracking без удержания в памяти
const nodeA = document.createElement('div');
processNode(nodeA); // обрабатывается
processNode(nodeA); // пропускается
// nodeA = null → GC удалит из processedNodes
// WeakSet методы: add, has, delete — нет size, нет итерации
Сравнение с Map/Set
// Map — сильная ссылка (объект не будет собран GC)
const cache = new Map();
let obj = { data: 'large dataset' };
cache.set(obj, 'processed');
obj = null; // obj всё ещё в памяти через cache!
// WeakMap — слабая ссылка (GC может собрать объект)
const weakCache = new WeakMap();
let obj2 = { data: 'large dataset' };
weakCache.set(obj2, 'processed');
obj2 = null; // obj2 может быть собран GC — запись удалится
Частые ошибки
- Попытка перечислить WeakMap/WeakSet — нет методов
keys,values,forEach, свойстваsize. Это намеренно: размер непредсказуем из-за GC. - Использование примитивов как ключей —
weakMap.set(42, 'val')→TypeError. Только объекты и символы (ES2023) могут быть ключами. - Ожидание синхронного удаления — GC работает по своему расписанию. Запись может оставаться в памяти некоторое время после обнуления ссылки на объект.
Связанные темы
Ресурсы
⚡ Источник: Мне нужно знать о JavaScript WeakRef и WeakMap · AsForJS
- 📅 2025-10-31 · YouTube
- Тезисы:
- WeakMap/WeakSet — единственные нативные структуры с слабыми ссылками на ключи
- Ключ — только object/symbol. Примитивы (string, number) запрещены — у них нет identity
- НЕТ итерируемости, НЕТ
size— намеренно, иначе нарушится семантика GC - Главный use case: метаданные привязанные к объектам без удержания их в памяти