Skip to content

JavaScript Performance

Writing efficient JavaScript code is crucial for creating fast and responsive web applications. This section covers key strategies to optimize your JavaScript performance.

Loops are common, and any inefficiency inside a loop is magnified by the number of iterations. Optimize by moving computations or property accesses out of the loop if they don’t change with each iteration.

Inefficient example (accessing arr.length repeatedly):

// Bad: arr.length is accessed in each iteration
// const arr = new Array(1000).fill(0);
// for (let i = 0; i < arr.length; i++) {
// // Do something
// }

More efficient (caching arr.length):

const arr = new Array(1000).fill(0);
const len = arr.length; // Cache the length
for (let i = 0; i < len; i++) {
// Do something
}
// Modern JavaScript engines are often smart enough to optimize this automatically,
// but for very large arrays or performance-critical code, caching can still be beneficial.

Accessing and manipulating the Document Object Model (DOM) is significantly slower than other JavaScript operations. Minimize DOM interactions:

  • Cache DOM elements: If you need to access a DOM element multiple times, query it once and store it in a variable.
  • Batch DOM updates: Instead of making many small changes to the DOM, try to make them in fewer operations. For example, build up an HTML string or a DocumentFragment in memory and then append it once.
  • Avoid layout thrashing: Repeatedly reading layout properties (like offsetHeight or getComputedStyle) and then writing to the DOM in a loop can force the browser to recalculate layout multiple times, which is very costly. Group reads and writes if possible.

Example of caching a DOM element:

// Inefficient:
// document.getElementById("myElement").innerText = "First update";
// document.getElementById("myElement").style.color = "blue";
// Efficient:
// const myElement = document.getElementById("myElement");
// if (myElement) {
// myElement.innerText = "First update";
// myElement.style.color = "blue";
// }

Using DocumentFragments for batch updates:

// const list = document.getElementById("myList");
// const items = ["Apple", "Banana", "Cherry"];
// const fragment = document.createDocumentFragment();
// items.forEach(itemText => {
// const li = document.createElement("li");
// li.textContent = itemText;
// fragment.appendChild(li);
// });
// list.appendChild(fragment); // Single append operation to the live DOM

A large DOM tree (many HTML elements) slows down page rendering, CSS styling, and DOM manipulation. Aim for a lean DOM structure.

This not only improves initial page load time but also makes subsequent DOM queries and manipulations faster.

The old advice was “Don’t create new variables if you don’t plan to save values.” For example:

// Old style:
// const firstName = "John";
// const lastName = "Doe";
// const fullName = firstName + " " + lastName;
// document.getElementById("demo").innerHTML = fullName;

Could be written as:

// const firstName = "John";
// const lastName = "Doe";
// document.getElementById("demo").innerHTML = firstName + " " + lastName;

However, modern JavaScript engines are very good at optimizing variable allocation. Prioritize code readability and maintainability. Well-named intermediate variables can make code easier to understand. Only micro-optimize variable usage if profiling shows it’s a bottleneck in a critical path.

5. Efficient JavaScript Loading Strategies

Section titled “5. Efficient JavaScript Loading Strategies”

How and when JavaScript is loaded can significantly impact page load performance and perceived speed.

  • Place scripts at the bottom of <body>: This allows the browser to parse and render the HTML content first before encountering blocking script tags.
  • Use async attribute: <script async src="script.js"></script> downloads the script asynchronously (without blocking HTML parsing) and executes it as soon as it’s downloaded. Order of execution is not guaranteed if multiple async scripts are present. Good for independent scripts like analytics.
  • Use defer attribute: <script defer src="script.js"></script> downloads the script asynchronously but executes it in order after the HTML document has been fully parsed, just before the DOMContentLoaded event. This is often the best choice for non-critical scripts that need the DOM to be ready and should execute in a specific order.
  • Dynamic Script Loading: Load scripts programmatically when needed, e.g., after user interaction or when a certain part of the page becomes visible.

Example of dynamic loading:

// function loadMyScript() {
// const script = document.createElement("script");
// script.src = "myHeavyScript.js";
// document.body.appendChild(script);
// }
// // Call loadMyScript() when appropriate, e.g., on a button click.

The with keyword extends the scope chain, which can make it harder for JavaScript engines to optimize code and can lead to ambiguity in variable resolution. It also negatively impacts performance.

with is forbidden in strict mode and is generally considered bad practice. Avoid it entirely.

  • Use efficient algorithms and data structures: Choose the right tools for the job. For example, Map or Set can be more performant than plain objects or arrays for certain operations (e.g., frequent additions/deletions or checking for existence).
  • Debouncing and Throttling: For event handlers that fire frequently (e.g., scroll, resize, input), use debouncing (executes the function only after a certain period of inactivity) or throttling (executes the function at most once per specified interval) to limit the number of calls.
  • Memoization: Cache the results of expensive function calls and return the cached result if the same inputs occur again.
  • Web Workers: For CPU-intensive tasks that don’t need DOM access, offload them to Web Workers to run in a separate thread, keeping the main UI thread responsive.
  • Code Splitting and Tree Shaking: For larger applications, use bundlers (like Webpack, Rollup, Parcel) that support code splitting (breaking your code into smaller chunks loaded on demand) and tree shaking (eliminating unused code from your final bundle).
  • Profile your code: Use browser developer tools (Performance tab) to identify bottlenecks in your JavaScript. Don’t optimize prematurely; focus on areas that are actually slow.