WeakSet и WeakMap

WeakSet и WeakMap

Технический фундамент слабых ссылок

WeakMap/WeakSet работают вокруг жизненного цикла объектов:

  • ключи/элементы не удерживают объект "вечно";
  • удаление происходит не вручную, а когда GC сочтет объект недостижимым;
  • поэтому структура неперечисляема и не дает size (состав нестабилен во времени).

Это инструменты для memory-safe привязок, а не для обычного хранения бизнес-данных.

Зачем нужны WeakSet и WeakMap

Обычные Set и Map удерживают ссылки на объекты, пока ты сам их не удалишь. В некоторых сценариях это мешает сборке мусора и может раздувать память. WeakSet и WeakMap решают эту задачу: они хранят слабые ссылки на объекты.

Ключевой момент: если на объект больше нет сильных ссылок, он может быть удален сборщиком мусора даже если был в WeakMap/WeakSet.

Проверь себя: почему кэш на базе Map может течь по памяти при долгой работе сервера?

WeakMap: слабые ключи-объекты

WeakMap похож на Map, но:

  • ключи только объекты;
  • нельзя итерироваться;
  • нет size.
const meta = new WeakMap();
let user = { id: 1, name: 'Anna' };

meta.set(user, { lastSeen: Date.now() });
console.log(meta.get(user));

user = null; // объект может быть собран GC

Смотри, что важно: WeakMap хорошо подходит для хранения "привязанных" метаданных к объектам, не продлевая их жизнь в памяти.

WeakSet: слабая коллекция объектов

WeakSet хранит только объекты и тоже не поддерживает итерацию.

const visited = new WeakSet();

const node = { value: 'root' };
visited.add(node);

console.log(visited.has(node)); // true

Когда node станет недостижимым, запись может быть автоматически очищена GC.

Почему нет size и for...of

Здесь часто путаются: в WeakMap/WeakSet нельзя узнать точный размер и нельзя пройтись по элементам. Причина в том, что GC удаляет объекты в неопределенный момент, и структура не гарантирует стабильный состав.

Проверь себя: можно ли строить UI-таблицу "всех элементов" из WeakMap? Почему нет?

Реальные микро-сценарии

  1. Приватные данные для объектов.

WeakMap часто используют, чтобы хранить внутренние данные экземпляров без прямого доступа снаружи.

  1. Отметка "уже обработан" для DOM-узлов.

WeakSet помогает помечать элементы, не удерживая их после удаления из DOM.

  1. Кэш вычислений для объектов.

Если объект исчезает, связанный кэш исчезает автоматически.

const cache = new WeakMap();

function getConfigFor(obj) {
  if (!cache.has(obj)) {
    cache.set(obj, { ready: true });
  }
  return cache.get(obj);
}

Дополнительный пример: приватные метаданные экземпляров через WeakMap.

const privateData = new WeakMap();

class User {
  constructor(name) {
    privateData.set(this, { createdAt: Date.now(), name });
  }

  getName() {
    return privateData.get(this).name;
  }
}

Частые ошибки новичков

  • Пытаться использовать примитив в ключе WeakMap.
  • Ждать от WeakSet метода size.
  • Писать бизнес-логику, которая требует перебора элементов WeakMap.
  • Использовать Weak* там, где нужен обычный перечисляемый кэш.

Анти-провал: если тебе нужно перечисление, размер и полный контроль данных - бери Map/Set. Если важна "мягкая" привязка к объекту без утечек - WeakMap/WeakSet.

Что будет, если изменить входные данные

Если попробовать weakMap.set('id', 1), будет ошибка, потому что ключ не объект. Если передать объект, запись сохранится корректно. Если потом потерять последнюю ссылку на этот объект, GC сможет очистить его вместе с записью.

Проверь себя: почему эта модель хорошо подходит для метаданных DOM-элементов в больших интерфейсах?

Краткий итог

  • WeakMap и WeakSet работают только с объектами и используют слабые ссылки.
  • Они помогают избегать утечек памяти в долгоживущих приложениях.
  • У них нет итерации и size, это осознанное ограничение дизайна.
  • WeakMap удобен для метаданных, WeakSet - для отметок обработанных объектов.
  • Выбирай Weak* только там, где нужна связь с жизненным циклом объекта.