Running Code Locally
Performance testing requires a real browser environment. Download Node.js to run JavaScript, use Chrome DevTools Performance tab for profiling, or create a .html file to test DOM operations.
JavaScript Performance Optimization
Master event loop mechanics, DOM optimization, memory management, and production-grade performance patterns.
What You'll Learn
- Avoiding main thread blocking
- Debouncing & throttling
- DOM optimization patterns
- GPU-accelerated animations
- Memoization & data structures
- Memory leak prevention
The Golden Rule: Avoid Blocking the Main Thread
JavaScript runs on a single-threaded event loop. Any task longer than 50ms is considered "slow" and causes frame drops. The key is breaking heavy work into smaller chunks.
Avoiding Main Thread Blocking
Break heavy work into smaller chunks to keep UI responsive
// Understanding the Main Thread
// JavaScript runs on a SINGLE thread - long tasks block EVERYTHING
// ❌ BAD - This blocks the UI for seconds
function blockingTask() {
const start = Date.now();
// Simulate heavy computation
let count = 0;
for (let i = 0; i < 100000000; i++) {
count += Math.sqrt(i);
}
console.log("Blocking task took:", Date.now() - start, "ms");
return count;
}
// ✅ GOOD - Break into chunks using setTimeout
function nonBlockingTask(callback) {
const chunkSi
...Debouncing & Throttling Events
Events like scroll, resize, and input can fire hundreds of times per second. Debounce waits for activity to stop; Throttle limits execution rate.
Debouncing & Throttling
Control event frequency for scroll, resize, and input
// Debouncing & Throttling - Essential for Event Performance
// DEBOUNCE: Wait until user STOPS doing something
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
// Cancel previous timer
clearTimeout(timeoutId);
// Set new timer
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// THROTTLE: Run at most once per interval
function throttle(fn, limit) {
let lastCall = 0;
return function(...args) {
const n
...DOM Optimization — The DOM is Slow
DOM operations are some of the slowest parts of JavaScript. Batch updates, use DocumentFragment, and minimize layout calculations.
DOM Optimization
Batch updates and use DocumentFragment for faster rendering
// DOM Optimization - The DOM is SLOW!
// ❌ BAD - Adding elements one by one (1000 layout updates)
function slowAppend(container) {
console.time("Slow append");
for (let i = 0; i < 1000; i++) {
const div = document.createElement("div");
div.textContent = "Item " + i;
container.appendChild(div); // Layout update each time!
}
console.timeEnd("Slow append");
}
// ✅ GOOD - Using DocumentFragment (1 layout update)
function fastAppend(container) {
console.time("Fast append");
...Layout Thrashing — The Silent Killer
Alternating between reading and writing layout properties forces the browser to recalculate layout on every operation. This can make code 20-100× slower.
Layout Thrashing
Batch reads and writes to avoid forced layout recalculations
// Layout Thrashing - The Silent Performance Killer
// ❌ BAD - Forces layout calculation on EVERY iteration
function layoutThrashing(boxes) {
console.time("Layout thrashing");
for (let i = 0; i < boxes.length; i++) {
// READ - forces layout calculation
const width = boxes[i].offsetWidth;
// WRITE - invalidates layout
boxes[i].style.width = (width + 10) + "px";
// Next read will force ANOTHER layout!
}
console.timeEnd("Layout thrashing");
}
// ✅ GOOD - Bat
...GPU-Accelerated Animations
Animating transform and opacity runs on the GPU and is incredibly fast. Animating top, left, width triggers expensive layout.
GPU-Accelerated Animations
Use transform and opacity for smooth, hardware-accelerated animations
// GPU-Accelerated Animations
// ❌ SLOW - Animating layout properties
// These trigger layout + paint on EVERY frame
const slowAnimationProperties = [
"top", "left", "right", "bottom",
"width", "height", "margin", "padding"
];
// ✅ FAST - GPU-accelerated properties
// These only trigger composite - runs on GPU!
const fastAnimationProperties = [
"transform", // translateX, translateY, scale, rotate
"opacity" // fade in/out
];
// Example: Smooth animation using requestAnimationFram
...Memoization — Cache Expensive Results
If something is expensive, compute it once and cache the result. Memoization can turn exponential time complexity into linear.
Memoization
Cache expensive calculations to avoid redundant computation
// Memoization - Cache Expensive Calculations
// Basic memoization function
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("Cache hit for:", key);
return cache.get(key);
}
console.log("Computing for:", key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Expensive calculation
function fibonacci(n) {
if
...Efficient Data Structures
Choosing the wrong data structure can cause massive performance loss. Use Map for key-value, Set for membership testing.
Efficient Data Structures
Use Map and Set for O(1) lookups and membership testing
// Efficient Data Structures
// Object vs Map for lookups
console.log("=== Object vs Map Performance ===");
// Object lookup
const userObject = {};
for (let i = 0; i < 10000; i++) {
userObject["user_" + i] = { id: i, name: "User " + i };
}
// Map lookup (FASTER for frequent operations)
const userMap = new Map();
for (let i = 0; i < 10000; i++) {
userMap.set("user_" + i, { id: i, name: "User " + i });
}
console.log("Object has prototype chain overhead");
console.log("Map is optimized for
...Memory Management & Leak Prevention
Memory leaks cause apps to slow down and crash. Always clean up event listeners, clear timers, and avoid capturing large data in closures.
Memory Management
Prevent memory leaks by cleaning up listeners and timers
// Memory Management & Garbage Collection
// ❌ Memory Leak #1: Forgotten Event Listeners
function leakyComponent() {
const handler = () => console.log("Click!");
// Listener added but never removed
document.addEventListener("click", handler);
// When component is destroyed, listener remains!
}
// ✅ Fixed: Clean up listeners
function cleanComponent() {
const handler = () => console.log("Click!");
document.addEventListener("click", handler);
// Return cleanup function
...Microtasks vs Macrotasks
Understanding the event loop is crucial for performance. Microtasks (Promises) run before rendering; macrotasks (setTimeout) run after.
Microtasks vs Macrotasks
Understand how Promise callbacks differ from setTimeout
// Microtasks vs Macrotasks
// MICROTASKS (run BEFORE next render):
// - Promise callbacks
// - queueMicrotask()
// - MutationObserver
// MACROTASKS (run AFTER render):
// - setTimeout
// - setInterval
// - setImmediate
// - I/O callbacks
// - UI rendering
console.log("=== Event Loop Demo ===");
console.log("1. Synchronous code");
setTimeout(() => {
console.log("4. setTimeout (macrotask)");
}, 0);
Promise.resolve().then(() => {
console.log("3. Promise.then (microtask)");
});
queueMicr
...Lazy Loading & Virtual Scrolling
Only load what the user needs right now. Dynamic imports, lazy images, and virtual scrolling can dramatically improve perceived performance.
Lazy Loading & Virtual Scrolling
Load only what's needed for better perceived performance
// Lazy Loading & Code Splitting
// 1. Dynamic Imports - Load code when needed
async function loadHeavyModule() {
// Module only loads when this function is called
const module = await import("./heavy-module.js");
return module.default;
}
// 2. Lazy Image Loading
function lazyLoadImages() {
const images = document.querySelectorAll("img[data-src]");
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
cons
...Performance Profiling & Measurement
You can't optimize what you can't measure. Use the Performance API, console.time, and Chrome DevTools to find bottlenecks.
Performance Profiling
Measure and find bottlenecks with Performance API and DevTools
// Performance Profiling & Measurement
// 1. Performance.now() for precise timing
function measureExecution(fn, label) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(label + ":", (end - start).toFixed(2) + "ms");
return result;
}
// 2. Console timing
function timeWithConsole() {
console.time("operation");
// ... do work ...
let sum = 0;
for (let i = 0; i < 1000000; i++) sum += i;
console.timeEnd("operation");
return sum;
...⚡ Performance Mastery Summary
- Main Thread: Keep tasks under 50ms, break heavy work into chunks
- Debounce/Throttle: Control event frequency for scroll, resize, input
- DOM: Batch updates, use DocumentFragment, avoid layout thrashing
- Animations: Use transform/opacity for GPU acceleration
- Memoization: Cache expensive calculations
- Data Structures: Use Map/Set for O(1) operations
- Memory: Clean up listeners, timers, and closures
- Event Loop: Use setTimeout to yield to rendering
- Lazy Loading: Load only what's needed, virtualize long lists
- Profiling: Measure before optimizing with DevTools
Sign up for free to track which lessons you've completed and get learning reminders.