Comprende cómo funciona el recolector de basura, usa referencias débiles para evitar fugas y suaviza el trabajo costoso con debounce y throttle.
Abrir esta lección en KodokonEl recolector de basura de V8 se basa en la alcanzabilidad: un objeto se vuelve recolectable en cuanto ninguna cadena de referencias lo enlaza con las raíces (la pila, los ámbitos activos, los globales). El GC es generacional: los objetos jóvenes viven en la *nursery*, recolectada a menudo y rápidamente (scavenge); los supervivientes se promueven a la generación antigua, gestionada por un mark-and-sweep incremental. Las fugas clásicas no provienen del GC sino de referencias que olvidaste: cachés basadas en Map, listeners que nunca se desvinculan, closures que capturan objetos grandes.
const strongCache = new Map();
const weakCache = new WeakMap();
let session = { user: "ada" };
strongCache.set(session, "data");
weakCache.set(session, "data");
session = null;
// Map: the key retains the object -> memory leak
// WeakMap: the entry becomes collectableUn WeakMap no retiene sus claves: cuando una clave deja de ser alcanzable desde cualquier otro lugar, la entrada entera desaparece. Consecuencias de diseño: las claves deben ser objetos (o símbolos no registrados), y la estructura no es ni iterable ni medible - exponer su tamaño revelaría el comportamiento del GC, que no es determinista. WeakRef va más allá: te da una referencia débil directa, que se desreferencia con deref().
let config = { theme: "dark" };
const ref = new WeakRef(config);
function readTheme() {
const target = ref.deref();
return target ? target.theme : "default";
}
console.log(readTheme()); // "dark"
config = null;
// After a GC pass, deref() may
// return undefined: plan a fallback.Del lado del rendimiento percibido, los eventos de alta frecuencia (scroll, resize, input, mousemove) pueden desencadenar cientos de llamadas por segundo. Dos estrategias complementarias: debounce solo ejecuta la función después de un periodo de calma (ideal para la búsqueda mientras se escribe), mientras que throttle garantiza como máximo una ejecución por intervalo (ideal para seguir el desplazamiento).
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(
() => fn.apply(this, args),
delay
);
};
}function throttle(fn, interval) {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn.apply(this, args);
}
};
}ref.deref() sobre un WeakRef?undefined si ha sido recolectadonull si el objeto ha sido recolectado