Перейти к содержимому

WeakMap / WeakSet / WeakRef / FinalizationRegistry

Weak-коллекции и WeakRef позволяют хранить ссылки на объекты без предотвращения их сборки GC — ключевой инструмент для кэшей, private-данных классов и мемоизации без memory leaks.

// WeakMap: ключи — только объекты, не перечисляем, не блокирует GC
const 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: слабая ссылка — не препятствует GC
let 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 cache
class 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 не детерминирован и не должен влиять на корректность программы.