Lección 50: WeakMap y WeakSet
🧠 Concepto
Section titled “🧠 Concepto”WeakMap y WeakSet son versiones “débiles” de Map y Set que permiten que los objetos sean recolectados por el garbage collector cuando ya no hay otras referencias a ellos.
WeakMap
Section titled “WeakMap”Características principales:
- Solo acepta objetos como claves (no primitivos)
- Las referencias a las claves son débiles: si no hay otras referencias al objeto, puede ser recolectado
- No tiene
.size(no sabemos cuántos elementos hay) - No es iterable (no podemos recorrerlo)
- Métodos:
.set(),.get(),.has(),.delete()
const weakMap = new WeakMap();
let usuario = { nombre: 'Ana' };weakMap.set(usuario, 'información privada');
console.log(weakMap.has(usuario)); // trueconsole.log(weakMap.get(usuario)); // "información privada"
// Si eliminamos la referencia al objeto...usuario = null;// ... el WeakMap libera la entrada automáticamente (GC)// No podemos verificarlo porque no hay .size ni iteraciónWeakSet
Section titled “WeakSet”Características similares a WeakMap:
- Solo acepta objetos (no primitivos)
- Referencias débiles
- No tiene
.size - No es iterable
- Métodos:
.add(),.has(),.delete()
const weakSet = new WeakSet();
let elemento = { id: 1 };weakSet.add(elemento);
console.log(weakSet.has(elemento)); // true
elemento = null; // el WeakSet libera la entrada💻 Ejemplo
Section titled “💻 Ejemplo”// Caso de uso: metadatos privados para objetosconst metadatos = new WeakMap();
class Usuario { constructor(nombre) { this.nombre = nombre; // Guardar datos privados en WeakMap metadatos.set(this, { creado: new Date(), ultimoAcceso: null, contadorAccesos: 0 }); }
acceder() { const datos = metadatos.get(this); datos.ultimoAcceso = new Date(); datos.contadorAccesos++; return `Bienvenido ${this.nombre} (acceso #${datos.contadorAccesos})`; }
getInfoPrivada() { return metadatos.get(this); }}
const usuario = new Usuario('Ana');console.log(usuario.acceder()); // "Bienvenido Ana (acceso #1)"console.log(usuario.acceder()); // "Bienvenido Ana (acceso #2)"console.log(usuario.getInfoPrivada()); // { creado: Date, ultimoAcceso: Date, contadorAccesos: 2 }
// Cuando usuario deje de existir, el WeakMap libera su metadata automáticamente
// Caso de uso: evitar fugas de memoria con listenersconst listeners = new WeakMap();
function agregarListener(elemento, evento, callback) { if (!listeners.has(elemento)) { listeners.set(elemento, new Map()); } const elementoListeners = listeners.get(elemento); elementoListeners.set(evento, callback); elemento.addEventListener(evento, callback);}
// Cuando el elemento se elimina del DOM, los listeners se limpian solos⚠️ Nota
Section titled “⚠️ Nota”¿Por qué no tienen .size ni iteración? Porque las referencias débiles pueden ser recolectadas en cualquier momento. Si pudieras iterar, verías entradas que desaparecen mientras iteras — inconsistencia.
No confundir “débil” con “débilmente tipado”. WeakMap/WeakSet se refiere a las referencias débiles para el garbage collector.
Los tipos primitivos como claves en WeakMap dan error:
const wm = new WeakMap();// wm.set('string', 1); // TypeError: Invalid value used as weak map key// wm.set(123, 1); // TypeErrorCasos de uso reales para WeakMap/WeakSet:
- Datos privados para objetos sin exponerlos (el patrón de arriba)
- Evitar fugas de memoria en cachés de objetos DOM
- Marcar objetos sin modificar el objeto original (WeakSet)
- Metadatos asociados a elementos que se crean y destruyen dinámicamente
// Marcar objetos visitados (sin modificar el objeto)const visitados = new WeakSet();
function visitar(objeto) { if (visitados.has(objeto)) { console.log('Ya visitado'); } else { visitados.add(objeto); console.log('Visitando por primera vez'); }}
const obj1 = { id: 1 };const obj2 = { id: 2 };
visitar(obj1); // "Visitando por primera vez"visitar(obj1); // "Ya visitado"visitar(obj2); // "Visitando por primera vez"📝 Ejercicio
Section titled “📝 Ejercicio”Crea un sistema de caché con WeakMap que almacene resultados de cálculos para objetos. Cuando el objeto se elimina, el caché debe liberarse automáticamente.
const calculosCache = new WeakMap();
function calcularComplejo(objeto, numero) { // Si ya existe el resultado en caché para este objeto, devuélvelo // Si no, calcula (numero * numero), guárdalo en caché y devuélvelo // Tu código aquí}
const obj = { nombre: 'test' };console.log(calcularComplejo(obj, 5)); // 25 (calculado)console.log(calcularComplejo(obj, 5)); // 25 (desde caché — sin calcular)