5 More JavaScript Performance Gotchas
Tags: javascript, performance, node
Published: 2023-04-11 10:02:06

As web applications continue to grow in complexity, understanding JavaScript performance pitfalls becomes increasingly important. In a [previous blog post](https://dev.to/andreasbergstrom/javascript-performance-gotchas-avoid-doing-this-cgm), we discussed several performance gotchas in JavaScript. In this follow-up post, we'll explore five more performance issues with code examples to help you write even more efficient code.

## Inefficient use of data structures

Choosing the right data structure for a specific task can have a significant impact on performance. For example, using an array instead of a Set to store unique values can lead to slower lookups and insertions.

```javascript
// Using an array to store unique values
const uniqueValuesArray = [];
if (!uniqueValuesArray.includes(value)) {
  uniqueValuesArray.push(value);
}

// Using a Set to store unique values
const uniqueValuesSet = new Set();
uniqueValuesSet.add(value);
```

**Verdict:** Be mindful of the data structures you use, and choose the most appropriate one based on the problem you're solving. Utilize built-in structures like Set and Map when they better fit your needs.

## Not optimizing DOM manipulations

Manipulating the Document Object Model (DOM) can be slow, especially when working with large amounts of data. Frequent DOM updates can cause layout thrashing and decrease performance. Most webapp use a framework that handles this, but its valuable to know the performance issue in vanilla Javascript as well.

```javascript
// Instead of appending elements one by one
const list = document.getElementById("list");
data.forEach(item => {
  const li = document.createElement("li");
  li.textContent = item;
  list.appendChild(li);
});

// Use a document fragment to batch updates
const list = document.getElementById("list");
const fragment = document.createDocumentFragment();
data.forEach(item => {
  const li = document.createElement("li");
  li.textContent = item;
  fragment.appendChild(li);
});
list.appendChild(fragment);
```

**Verdict:** Minimize the number of DOM manipulations by using techniques such as document fragments, batching updates, or using a virtual DOM library like React. Consider using requestAnimationFrame for animation-related updates to ensure smooth rendering.

## Blocking the event loop

Running CPU-intensive tasks or long-running synchronous code on the main thread can block the event loop and cause the browser to become unresponsive.

```javascript
// Blocking the main thread
function processData(data) {
  // CPU-intensive task
}

// Offloading the task to a Web Worker
const worker = new Worker("worker.js");
worker.postMessage(data);
worker.onmessage = function (event) {
  const result = event.data;
  // Process the result
};
```

**Verdict:** Use techniques like Web Workers or asynchronous functions (e.g., Promises and async/await) to offload heavy tasks and keep the main thread responsive.

## Inefficient event handling

Adding a large number of event listeners to individual DOM elements can cause performance problems and increase memory usage. Event delegation can help reduce the number of event listeners and improve performance.

```javascript
// Adding event listeners to individual elements
const buttons = document.querySelectorAll("button");
buttons.forEach(button => {
  button.addEventListener("click", event => {
    // Handle the click event
  });
});

// Using event delegation
const container = document.getElementById("container");
container.addEventListener("click", event => {
  if (event.target.tagName === "BUTTON") {
    // Handle the click event
  }
});
```

**Verdict:** Use event delegation to minimize the number of event listeners and improve performance. Instead of attaching event listeners to each element, attach a single listener to a common parent element and use the event target to determine which child element was interacted with.

## Inefficient regular expressions

Poorly written regular expressions can cause significant performance issues, especially when processing large strings. Some common problems include excessive backtracking, catastrophic backtracking, and inefficient character classes.

```javascript
// Poorly written regular expression
const regex1 = /(a+)+b/; // Can lead to catastrophic backtracking on strings like "aaaaaaaaaaaaaaaaaaaaaaaaaaaaac"

// Optimized regular expression
const regex2 = /(?:a+)+b/; // Non-capturing group prevents excessive backtracking

// Efficient character class
const regex3 = /\d/; // Matches any digit

// Inefficient character class
const regex4 = /[0-9]/; // Same as \d, but less efficient
```

**Verdict:** Optimize your regular expressions by using non-greedy quantifiers, atomic groups, and possessive quantifiers when appropriate. Test your regular expressions with various inputs to ensure they perform well in all scenarios.

Being aware of these additional performance gotchas and using the appropriate techniques can help you further improve the performance of your JavaScript applications. As always, profile and optimize your code based on real-world data to achieve the best results. Keep learning and refining your skills to write fast, efficient, and maintainable JavaScript code.