Performance
Debounce & throttle 🎯, Memory leaks — common causes, Loading & rendering
Debounce & throttle 🎯#
ELI12. Debounce = "wait until the user stops, then act once" (search box). Throttle = "act at most once every N ms no matter how often it fires" (scroll/resize).
function debounce(fn, delay) {
let t;
return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), delay); };
}
function throttle(fn, limit) {
let waiting = false;
return (...args) => {
if (waiting) return;
fn(...args); waiting = true;
setTimeout(() => (waiting = false), limit);
};
}
Memory leaks — common causes#
- Forgotten
setInterval/ listeners on removed elements. - Detached DOM nodes still referenced in JS.
- Ever-growing caches/closures → use
WeakMap/ cap size (LRU). - Global variables holding large data.
Detect with DevTools Memory tab (heap snapshots, allocation timeline).
Loading & rendering#
- Lazy loading: dynamic
import(),<img loading="lazy">,IntersectionObserver. - Code splitting: bundlers split routes/features into separate chunks.
- Memoization: cache pure computations (Functions).
- Virtual DOM / virtualization: frameworks diff a lightweight tree; list virtualization renders only visible rows.
- Avoid layout thrashing: batch DOM reads then writes; use
requestAnimationFrame. - Profiling: DevTools Performance panel,
performance.now(),PerformanceObserver.
const t0 = performance.now();
work();
console.log(performance.now() - t0, "ms");
Interview Q&A#
Q1. Debounce vs throttle — implement both, give a use for each. Debounce waits until events stop firing, then runs once (great for a search-box on keypress); throttle runs at most once per interval no matter how often it fires (great for scroll/resize handlers).
const debounce = (fn, d) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), d); }; };
const throttle = (fn, l) => { let w = false; return (...a) => { if (w) return; fn(...a); w = true; setTimeout(() => w = false, l); }; };
Q2. Name three memory-leak sources in a SPA and how you'd find them.
Forgotten setInterval/listeners on removed elements, detached DOM nodes still referenced in JS, and ever-growing caches or closures holding large data. Find them with DevTools' Memory tab — heap snapshots (compare over time) and the allocation timeline.
Q3. What is layout thrashing and how do you avoid it?
Layout thrashing is repeatedly interleaving DOM reads (which force synchronous layout/reflow) with writes in a loop, causing many redundant reflows. Avoid it by batching all reads first then all writes, and scheduling visual updates with requestAnimationFrame.