JavaScript garbage-collects automatically, but that only works if nothing still references an object you meant to discard — a handful of common patterns quietly keep references alive far longer than intended.
// 1. Forgotten event listeners on removed elements
element.addEventListener('click', handler);
element.remove(); // the listener (and its closure) may still be referenced elsewhere
// 2. Timers that are never cleared
const interval = setInterval(() => updateData(), 1000);
// component unmounts, but the interval keeps running and referencing it forever
// FIX: clearInterval(interval) on cleanup
// 3. Growing caches/maps with no eviction
const cache = new Map();
function memoize(key, value) {
cache.set(key, value); // grows forever if keys are never removed
}
// 4. Closures capturing more than they need
function setupHandler(hugeDataset) {
return () => console.log('clicked'); // still holds a reference to hugeDataset
// even though it never uses it!
}
Chrome DevTools’ Memory tab (specifically, comparing heap snapshots taken before and after an action that should release memory) is the definitive way to confirm a suspected leak rather than guessing from symptoms alone.