Event listeners
Every addEventListener registers the handler on the target (or capture root). The target holds the listener list — a retention path.
flowchart TD bus[EventTarget] h1[handler 1 + closure] h2[handler 2 + closure] data1[large payload A] data2[large payload B] bus --> h1 bus --> h2 h1 --> data1 h2 --> data2
Broken code
Section titled “Broken code”function subscribe(userId) { const profile = loadHugeProfile(userId); window.addEventListener('scroll', () => { track(profile.id); // profile retained by handler }); // re-called on every route visit → duplicate listeners}Fixed code
Section titled “Fixed code”const controller = new AbortController();
function subscribe(userId) { const profile = loadProfile(userId); window.addEventListener( 'scroll', () => track(profile.id), { signal: controller.signal } );}
function unsubscribe() { controller.abort(); // removes all listeners using this signal}Live demo
Section titled “Live demo”Live demo: duplicate event listeners
Each "subscribe" adds another listener that closes over large data.
`performance.memory` is only available in Chromium-based browsers. Use Chrome for live heap readouts.