WeakMap y WeakSet: colecciones con referencias débiles

Avanzado
JavaScript
JavaScript
Actualizado: 19/04/2026

Qué es una referencia débil

En JavaScript la memoria se libera mediante un recolector de basura (garbage collector, GC): un objeto sobrevive mientras alguien mantenga una referencia a él. Si un objeto queda "colgado" en un Map o un Set, aunque ya no lo uses en ninguna otra parte del código, sigue vivo porque la colección lo referencia.

Las referencias débiles son un tipo especial de referencia que no protegen al objeto frente al GC. Es decir: si la única forma de llegar a un objeto es a través de una referencia débil, el GC puede recogerlo en cualquier momento. WeakMap y WeakSet son las colecciones que emplean este tipo de referencia para sus claves (WeakMap) o elementos (WeakSet).

El efecto práctico es muy útil: podemos asociar datos a una instancia sin preocuparnos de limpiarlos manualmente cuando la instancia deje de existir.

WeakMap: clave → valor con clave débil

Un WeakMap funciona como un Map, pero con dos restricciones y una propiedad distintiva:

  • Las claves deben ser objetos (no primitivos).
  • Las entradas no son enumerables: no hay keys, values, entries, size ni forEach.
  • Las claves se referencian débilmente: si el objeto-clave deja de tener otras referencias, la entrada desaparece.
const metadata = new WeakMap();

let user = { id: 1, name: "Alan" };
metadata.set(user, { lastLogin: Date.now(), roles: ["admin"] });

console.log(metadata.has(user)); // true
console.log(metadata.get(user)); // { lastLogin: ..., roles: ["admin"] }

user = null; // La única referencia al objeto se pierde
// La entrada del WeakMap podrá ser recogida por el GC.

No existe un API para forzar la recolección, ni para comprobar cuándo ocurre. Simplemente: la implementación garantiza que no habrá fuga.

Caso práctico: caché por instancia

Imagina que quieres calcular algo caro a partir de un objeto y cachear el resultado. Un Map te obligaría a invalidar la entrada cuando dejaras de usar el objeto. Con un WeakMap no hace falta:

const cache = new WeakMap();

function getExpensiveData(user) {
  if (cache.has(user)) {
    return cache.get(user);
  }
  const data = computeExpensiveData(user); // Llamada costosa
  cache.set(user, data);
  return data;
}

function computeExpensiveData(user) {
  console.log(`Computing data for ${user.name}`);
  return { summary: `User ${user.name}` };
}

let alan = { name: "Alan" };
getExpensiveData(alan); // Computing data for Alan
getExpensiveData(alan); // cache hit, sin log

alan = null; // El usuario queda fuera de alcance
// La entrada del cache desaparecerá con el GC, sin código explícito de limpieza.

Caso práctico: metadatos "privados"

Antes de los campos privados (#campo), un patrón habitual para simular privacidad consistía en guardar el estado en un WeakMap exterior a la clase:

const privateState = new WeakMap();

class Counter {
  constructor(initial = 0) {
    privateState.set(this, { count: initial });
  }

  increment() {
    const state = privateState.get(this);
    state.count += 1;
  }

  get value() {
    return privateState.get(this).count;
  }
}

const c = new Counter(10);
c.increment();
console.log(c.value); // 11

Al hacerse a través del WeakMap, el estado no se expone como propiedad del objeto: no aparece en Object.keys, no viaja en JSON.stringify, y se libera automáticamente cuando la instancia desaparece.

WeakSet: pertenencia con referencia débil

WeakSet es la versión de Set con referencias débiles. Sus elementos deben ser objetos y tampoco se puede iterar ni consultar su tamaño.

const visited = new WeakSet();

function walk(node) {
  if (visited.has(node)) return; // Evita ciclos
  visited.add(node);

  for (const child of node.children ?? []) {
    walk(child);
  }
}

Al igual que con el WeakMap, si un nodo deja de usarse, su entrada en el WeakSet desaparecerá.

Caso práctico: marcar instancias como procesadas

Un uso habitual es registrar que un objeto ya ha sido inicializado o validado, sin tener que añadirle una propiedad especial:

const initialized = new WeakSet();

function init(obj) {
  if (initialized.has(obj)) return;
  // … trabajo de inicialización …
  initialized.add(obj);
}

const widget = { id: "w-1" };
init(widget); // primera vez: se inicializa
init(widget); // segunda vez: no-op

El objeto queda intacto (no añadimos propiedades), y la marca se pierde cuando el objeto desaparece.

Caso práctico: validar que el receptor es una instancia permitida

Con módulos que exportan clases y fábricas, a veces se usa un WeakSet como "carnet" para comprobar que un método recibe un objeto auténtico y no un duck-typed:

const officialInstances = new WeakSet();

export class Account {
  constructor(id) {
    this.id = id;
    officialInstances.add(this);
  }
}

export function withdraw(account, amount) {
  if (!officialInstances.has(account)) {
    throw new TypeError("Not an official Account instance");
  }
  // … retirada segura …
}

Diferencias con Map y Set

| Operación | Map / Set | WeakMap / WeakSet | |-----------|---------------|------------------------| | Claves / elementos | Cualquier tipo | Solo objetos | | Referencias | Fuertes | Débiles | | Iteración (for...of, forEach) | Sí | No | | size | Sí | No | | clear() | Sí | No | | Recolección automática | No | Sí, cuando el objeto deja de ser referenciado |

La tabla deja claro cuándo usar cada uno. Las versiones Weak se centran exclusivamente en el caso "asociar información a un objeto mientras exista", y para ganar esa garantía de recolección se renuncia a la enumeración.

Cuándo no usarlas

WeakMap y WeakSet no son sustitutos generales de Map/Set. Evítalos cuando:

  • Necesitas iterar o conocer el tamaño: no lo permiten.
  • Las claves son primitivos (cadenas, números, Symbols): no las admiten.
  • Necesitas serializar el contenido: al no ser iterables, no hay forma directa.
  • Quieres una caché con política propia de expiración (TTL, LRU…): usa Map y controla el ciclo de vida tú mismo.

Un error frecuente es intentar "contar" cuántas entradas tiene un WeakMap para inferir qué objetos siguen vivos. No es posible ni fiable: la recolección es asíncrona y no observable.

Buenas prácticas

  • Usa WeakMap cuando quieras asociar datos a un objeto sin modificarlo ni prolongar su vida.
  • Usa WeakSet para marcadores booleanos sobre objetos (procesado, visitado, instancia oficial).
  • No confíes en momentos concretos de recolección; el GC es libre de no ejecutarse nunca si no hace falta.
  • Si necesitas iterar o conocer el tamaño, cambia a Map/Set y encárgate tú de eliminar entradas obsoletas.
  • Para lógica más fina (notificación cuando un objeto es recogido), existe WeakRef + FinalizationRegistry, pero se recomienda solo para casos muy específicos (cachés de recursos externos) porque su comportamiento es intencionalmente no determinista.

Resumen

WeakMap y WeakSet son colecciones pensadas para un problema concreto: asociar información a objetos sin alargarles la vida. Sus referencias débiles permiten que el recolector de basura recupere los objetos cuando nadie más los usa, y con ellos desaparecen las entradas correspondientes. A cambio se pierde la capacidad de iterar o medir la colección. Son la herramienta correcta para cachés por instancia, metadatos privados y marcadores como visited o initialized; para cualquier otro caso, Map y Set siguen siendo la elección adecuada.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, JavaScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de JavaScript

Explora más contenido relacionado con JavaScript y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Entender qué es una referencia débil y cómo afecta al recolector de basura.
  • Diferenciar WeakMap/WeakSet de Map/Set en API y comportamiento.
  • Usar WeakMap para asociar metadatos privados a instancias sin fugas de memoria.
  • Usar WeakSet para marcar objetos visitados o con un atributo booleano.
  • Implementar una caché por objeto que se libere cuando desaparezca la clave.
  • Reconocer los límites: no se puede iterar ni conocer el tamaño.

Cursos que incluyen esta lección

Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje