JavaScript Performance Tips

Writing good JavaScript means faster load times and more dynamic web apps. While frameworks like Angular and React have largely abstracted the vanilla JavaScript away from modern day web development, it's still important to understand best practices for writing plain old JS. In this article, we discuss best practices and quick tips for writing JavaScript code that scales well.

Watch Your DOM Manipulation

Working with host objects like the DOM are some of the most expensive operations for JS. Anytime you use something like document.findElementById(), the JS engine must traverse the DOM tree to find the appropriate element. There are several things to keep in mind with DOM manipulation, including:

Keep Your DOM Light

The more elements you have in your DOM, the more elements your JS engine has to traverse to find what it is looking for. This is pretty straightforward however things can get out of hand quickly with nested elements and show/hide functionality. Always ask yourself if the element you're adding is necessary and if it can be represented with fewer HTML elements.

Keep Selectors Simple With IDs

While libraries like jQuery make css selectors convenient, it takes more time to locate whacky and complicated selectors than it does simple ID lookups. This is because IDs are unique. By selecting elements via a unique ID, you save the engine time traversing the DOM for the specified element with a more complicated selector like $("div > p > .myClass").

Store References to DOM Objects in Variables

Every time you call something like document.getElementById() you re essentially telling the interpreter to traverse the DOM and find the specified element. If you are going to be working with the same element again and again, it's best practice to save the reference in a variable for later use. For example:

var btn = document.getElementById("loginBtn");

Now you can simply reference the btn variable instead of traversing the DOM each time you want to use the specified element.

Minimize DOM Manipulation

Libraries like jQuery make DOM manipulation a breeze. While shortcut methods like .css() are convenient ways to update CSS properties for DOM elements, it's important to remember that JavaScript redraws the page for each manipulation. For this reason, it's crucial that you minimize the number of DOM manipulations you have. Combine when necessary to reduce the number of times you have to redraw the page.

Manage Dependencies

There are tons of libraries and APIs out there that greatly reduce the amount of boilerplate code you have to write. Examples include everything from jQuery to LoDash. While these libraries are great for saving time and providing what's "already been figured out", they can add a lot of unnecessary code to your project.

Webpack Saves The Day

Virtually all of the traditional issues experienced with JS dependencies have been solved with Webpack. Webpack is great for managing third party libraries and scripts you use in your project. Not only does it control load times, it also minifies scripts and optimizes client-side assets. For more on webpack see our tutorial on Webpack.

Watch Your VARs

Declaring variables with var is generally a good idea. It gives your variables local scope and prevents you from declaring =global variables. While global variables are sometimes necessary, they take longer to look up and can clutter the global namespace for your project.

With that said, be careful with how many variables you define. It's best practice to minimize the number of variables you use. For example

console.log(questionString + "?");

is better than

var question = questionString + "?";
console.log(question);

Watch Your Loops

Loops and iterations are always subject to performance issues. Be sure to iterate over only what's absolutely necessary to avoid expensive operations. For example, if you are referencing an element ( document.getElementById()) ) within a for loop, you should instead reference this as a var outside the loop as it's not necessary to reference the element for each iteration.

//bad practice
for(var i=0;i<1000;i++){
  var element = document.getElementById("el");
  console.log(element);
}

//good practice
var element = document.getElementById("el");
for(var i=0;i<1000;i++){  
  console.log(element);
}

Another common gotcha with loops is to define your iterator length outside of the for loop. For example:

var length = arr.length
for(var i=0; i < length; i++){
}

is better than:

for(var i=0; i < arr.length; i++){
}

When you reference the arr.length property directly in the loop definition the engine evaluates the length property for each iteration. This ultimately slows things down and is considered bad practice.

Monomorphic vs Polymorphic

This speaks more specifically to the V8 engine. While JavaScript is technically an interpreted language, Google's V8 engine uses just-in-time compilation to optimize the code you write. This means that data types start to cary some significance as V8 attempts to optimize functions that always take the same types of arguments. This concept is best explained through example. Take the following function:

function(a,b){
  return a + b;
}

If you consistently pass integers to this function, the V8 engine will optimize the function at compile time. If instead you pass in variables with different data types (aka strings, objects, etc) then the engine will "deoptimize" the function. For more information on the V8 engine see our discussion on Google's V8 engine.

Conclusion

These principles will keep your JavaScript code optimized and adhering to best practice. Although existing JS frameworks like React and Angular have largely addressed these principles through the implementation of their APIs, it's still good to understand these principles for your own JavaScript development.

Your thoughts?