← Back to Chapters

JavaScript Performance Optimization

⚡ JavaScript Performance Optimization

? Quick Overview

JavaScript performance optimization is about making your web applications faster, smoother, and more efficient. It focuses on reducing load times, avoiding unnecessary work, controlling memory usage, and keeping interactions responsive so users experience minimal lag or jank.

Well-optimized JavaScript leads to a better user experience, lower bounce rates, and can even improve SEO because search engines reward fast-loading, responsive pages.

? Key Concepts

  • Minimize DOM manipulations: Batch DOM updates instead of changing the DOM in tiny, frequent steps.
  • Efficient animations: Use requestAnimationFrame instead of setTimeout or setInterval for smoother animations.
  • Debouncing and throttling: Control how often expensive functions run for frequent events like scroll and resize.
  • Script loading strategy: Use async and defer or lazy loading so scripts do not block page rendering.
  • Caching and memoization: Store results of expensive computations to avoid recalculating them.
  • Memory management: Remove unused event listeners and references to prevent memory leaks.
  • Measure before optimizing: Use tools like Chrome DevTools to find real bottlenecks.

? Syntax and Theory

⏱️ Debouncing

Debouncing ensures a function runs only after a certain period of inactivity. It is ideal for search boxes, window resize, and scroll tracking where you do not want to run logic on every single event.

? Throttling

Throttling ensures a function runs at most once per defined interval. It is useful for continuous events like scrolling, where updates can happen every 100–200 ms instead of on every pixel move.

? Caching and Memoization

Memoization stores function results based on inputs. When the same input appears again, the stored value is returned instantly instead of recalculating, which is helpful for expensive operations.

? Async and Defer

When including scripts:

  • async loads the script in parallel and executes it as soon as it is ready.
  • defer loads the script in parallel but executes it after HTML parsing finishes.

? Code Examples

⏱️ Debouncing Scroll Event

? View Debounce Example
// Debounce helper: runs the function only after the user stops scrolling
function debounce(func, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, arguments), delay);
};
}

// Attach a debounced listener so the handler is not called on every scroll event
window.addEventListener('scroll', debounce(() => {
console.log('Scroll event triggered!');
}, 200));

? Caching with Memoization

? View Caching Example
// Simple memoized function that caches results based on the input value
function expensiveOperation(x) {
if (!expensiveOperation.cache) {
expensiveOperation.cache = {};
}
if (!expensiveOperation.cache[x]) {
console.log('Calculating for', x);
expensiveOperation.cache[x] = x * x; // example calculation
}
return expensiveOperation.cache[x];
}

console.log(expensiveOperation(5)); // Calculates and caches
console.log(expensiveOperation(5)); // Returns from cache

? Using Async and Defer

? View Script Loading Example
// Example of non-blocking script loading using async and defer
<!-- Async: downloads in parallel and executes as soon as ready -->
<script src="script-analytics.js" async></script>

<!-- Defer: downloads in parallel and executes after HTML parsing -->
<script src="script-main.js" defer></script>

? Live Output and Explanation

⏳ Debounced Search Demo

Type in the input below. The "Simulated request" text updates only when you stop typing for 500 ms. This mimics how debouncing can reduce the number of API calls.

Waiting for input...

Without debouncing, the handler would run on every keystroke. With debouncing, it runs only after you pause, which is much more efficient.

? Tips and Best Practices

  • Use Chrome DevTools (Performance, Lighthouse, Memory tabs) to find real bottlenecks.
  • Use code splitting so only the necessary JavaScript is loaded for the current page or route.
  • Clean up event listeners and intervals when elements are removed or components are unmounted.
  • Prefer smaller, focused functions and modules to keep code easier to analyze and optimize.
  • Cache frequently accessed DOM elements instead of querying them repeatedly.

? Try It Yourself

  • Implement a debounced resize event that logs the new window size only after the user stops resizing.
  • Create a memoized function for an expensive calculation (for example, factorial or Fibonacci) and log when it recalculates.
  • Take an existing page, open Chrome DevTools, record a performance profile, and identify which script or function is slowest.
  • Refactor a script to use defer or async and measure how it affects initial page load time.