WeakMap / WeakSet / WeakRef / FinalizationRegistry
Weak-коллекции и WeakRef позволяют хранить ссылки на объекты без предотвращения их сборки GC — ключевой инструмент для кэшей, private-данных классов и мемоизации без memory leaks.
// WeakMap: ключи — только объекты, не перечисляем, не блокирует GCconst computeCache = new WeakMap();function memoizedProcess(obj) { if (computeCache.has(obj)) return computeCache.get(obj); const result = expensiveCompute(obj); computeCache.set(obj, result); return result;}// Когда obj собирается GC → запись в computeCache исчезает автоматически
// Private данные через WeakMap (паттерн до ES2022 private fields)const _private = new WeakMap();class Secure { constructor(secret) { _private.set(this, { secret }); } verify(s) { return _private.get(this).secret === s; }}// Сейчас предпочтительнее: class { #secret; constructor(s) { this.#secret = s; } }// WeakRef: слабая ссылка — не препятствует GClet target = { data: new Array(1e6).fill(0) }; // большой объектconst ref = new WeakRef(target);target = null; // снимаем strong reference → GC может собрать
// Позже, возможно после GC:const live = ref.deref();if (live !== undefined) { console.log('объект жив, data.length:', live.data.length);} else { console.log('GC собрал объект');}// ВАЖНО: deref() может вернуть undefined в любой момент// Нельзя полагаться на то, что deref() вернёт объект между двумя вызовами// FinalizationRegistry: callback ПОСЛЕ того как GC собрал объектconst registry = new FinalizationRegistry((token) => { // token — это то, что вы передали при register, НЕ сам объект // объект уже собран GC, его здесь нет console.log('GC собрал объект с токеном:', token); externalResourcesMap.delete(token);});
let managed = { data: new Array(1e6) };registry.register(managed, 'managed-resource-id');managed = null; // → когда-то callback будет вызван
// Паттерн: WeakRef + FinalizationRegistry = self-cleaning cacheclass WeakCache { #cache = new Map(); #reg = new FinalizationRegistry(key => this.#cache.delete(key));
set(key, value) { this.#cache.set(key, new WeakRef(value)); this.#reg.register(value, key); } get(key) { return this.#cache.get(key)?.deref(); }}Итог: WeakMap/WeakSet — хранилища метаданных об объектах без блокировки GC; WeakRef + FinalizationRegistry дают тонкий контроль lifetime, но callback не детерминирован и не должен влиять на корректность программы.