20 Best Practices for Improving JavaScript Performance
JavaScript is an integral part of practically every webpage, mobile app and web-based software. While JavaScript's client side scripting capabilities can make applications more dynamic and engaging, it also introduces the possibility of inefficiencies by relying on the user's own browser and device. Consequently, poorly written JavaScript can make it difficult to ensure a consistent experience for all users.
This guide will explore the causes of JavaScript performance issues and provide a list of best practices for optimizing JavaScript code.
Common JavaScript performance problems
The first step to fixing any problem is identifying the root cause. Here are a few things that can cause JavaScript performance to falter:
1. Too many interactions with the host
Every interaction with the host object, or the user's browser, increases unpredictability and contributes to performance lag. This problem often manifests as slow rendering of DOM objects. You obviously can't avoid such interactions, but you can keep them to a minimum. Learning more about what can contribute to blocking the DOM and how to fix it.
2. Too many dependencies
If your JavaScript dependencies are plentiful and poorly managed, then your application's performance will suffer. Your users will have to wait longer for objects to render, which is especially annoying for mobile users with limited bandwidth.
3. Poor event handling
Proper use of event handlers can improve performance by reducing the depth of your call stack; however, if you don't keep track of them, they can execute repeatedly without your knowledge. Use them sparingly and wisely.
4. Inefficient iterations
Since iterations take up so much processing time, they provide a great starting point for optimizing your code. Removing unnecessary loops or calls within loops will speed up your JavaScript performance.
5. Unorganized code
JavaScript's loose nature is both an asset and a liability. You can do a lot with just a small subset of lexical constructs, but a lack of organization in your code can result in inadequate allocation of resources. Familiarizing yourself with ECMA standards can help you construct more concise JavaScript.
20 Tips and best practices for improving JavaScript performance
Now that we've discussed what can hinder JavaScript performance, let's look at some ways to give an application's JavaScript performance a boost:
1. Use HTTP/2
HTTP/2 is the latest version of the HTTP protocol and provides some great enhancements that will not only help improve your JavaScript performance but will also help speed up your site in general. HTTP/2 uses multiplexing, therefore allowing multiple requests and responses to be sent at the same time. If you haven't moved to HTTPS yet, be sure to do so as soon as possible to take advantage of the performance improvements that HTTP/2 has to offer.
2. Use pointer references
You can also cut down on DOM traversal trips by storing pointer references for in-browser objects during instantiation. If you don't expect your DOM to change, storing a reference to the DOM or jQuery objects needed to create your page can help speed everything along. Alternatively, if you need to iterate inside a function, but you haven't stored a reference, you can make a local variable with a reference to the object.
3. Trim your HTML
The complexity of your HTML plays a large role in determining how long it takes to query and modify DOM objects. If you can cut your application's HTML by half, you could potentially double your DOM speed. That's a tough goal, but you can start by eliminating unnecessary <div>
and <span>
tags.
4. Use document.getElementById()
Using jQuery lets you create specific selectors based on tag names and classes, but this approach necessitates several iterations as jQuery loops through DOM elements to find a match. You can speed up the DOM by using the document.getElementById()
method instead.
// With jQuery
var button = jQuery('body div.window > div.minimize-button:nth-child(3)')[0];
// With document.getElementById()
var button = document.getElementById('window-minimize-button');
5. Batch your DOM changes
Every time you make DOM changes, batch them up to prevent repeated screen rendering. If you're making style changes, try to make all of your modifications at once rather than applying changes to each style individually.
6. Buffer your DOM
If you have scrollable DIVs, you can use a buffer to remove items from the DOM that aren't currently visible inside the viewport. This technique helps you save on both memory usage and DOM traversal.
7. Compress your files
Use a compression method such as Gzip or Brotli to reduce the size of your JavaScript files. With a smaller sizes file, users will be able to download the asset faster, resulting in improved performance.
8. Limit library dependencies
Library dependencies add a lot to loading times, so strive to keep their use to a minimum, and avoid them entirely if at all possible. One way to reduce your dependency on external libraries is to rely more on in-browser technology.
Furthermore, if you need complex CSS selectors, try using Sizzle.js instead of jQuery. If you have libraries that contain just one feature, it makes more sense to just add that feature separately.
9. Minify your code
Bundling your application's components into *.js files and passing them through a JavaScript minification program will make your code leaner. Some popular code minification tools are listed at the end of this tutorial.
10. Add post-load dependency managers
Adding a dependency manager, like RequireJS or webpack, to your load scripts lets the user see your application's layout before it becomes functional. This can have a huge positive impact on conversions for first-time visitors. Just make sure your dependency manager is set up to track which dependencies have already been loaded, or else the same libraries could load twice. Always aim to load the absolute minimum the user needs to see.
11. Cache as much as you can
Caching is your greatest asset for speeding up load times. Ensure you leverage browser caching as well as intermediary caching mechanisms such as a content delivery network. This will ensure that your assets load quickly both for previous visitors as well as first time visitors.
12. Mind your event handlers
Since events like 'mousemove' and 'resize' execute hundreds of times per second, pay special attention to any event handlers bound to those events. If they take more than 2-3 milliseconds to complete, you need to better optimize your code.
13. Replace click
with mouseup
Binding your functionality to the mouseup
event, which fires before the click
event, provides a performance boost by ensuring that no interactions are missed if the user makes several mouse clicks in rapid secession.
14. Use reference types responsibly
While primitive value types like strings and integers get copied every time they are passed into a new function, reference types, like arrays and objects, are passed as lightweight references. Therefore, you can do things like pass DOM node references recursively to cut down on DOM traversal. Also, remember that comparing strings always takes longer than comparing references.
15. Cut down your scope chain
When functions are executed in JavaScript, a set of first order variables including the immediate scope chain, the arguments of the function and any locally-declared variables are instantiated. Therefore, it takes time to climb up the scope chain when you try to access a globally-declared variable. Reducing the the call stack's depth and taking advantage of the this
keyword can speed up execution.
16. Use the local scope (this
)
Speaking of which, this
allows you not only to write asynchronous code with callbacks, but it also helps boost performance by reducing dependency on global variables or closures residing higher in the scope chain. Conversely, you should avoid the with
keyword because it modifies the scope chain, which drags down performance. You can rewire the scope variable using the call()
and apply()
methods as follows:
var Person = Object.create({
init: function(name) {
this.name = name;
},
do: function(callback) {
callback.apply(this);
}
});
var bob = new Person('bob');
bob.do(function() {
alert(this.name); // 'bob' is alerted because 'this' was rewired
});
17. Favor native functions and constructs
Rather than writing your own algorithms or relying too much on host objects, take advantage of native functions and constructs as much as you can. ECMAScript lists hundreds of native constructs for you to choose from.
18. Prefer async
and defer
If you want script tags to load asynchronously, or to defer until the rest of the page has finished loading, you can add the async
or defer
attributes:
// load example.js without interrupting your webpage's rendering
<script src="example.js" async></script>
// load example.js after the page has finished loading
<script src="example.js" defer></script>
The graph below shows the difference between synchronous and asynchronous loading. As we can see, synchronous assets need to wait for the previous asset to finish loading before a new one can start; whereas asynchronously loaded assets can load at the same time.
19. Animate with requestAnimationFrame
Animations should ideally render at 60 FPS. JavaScript libraries like Anime.js and Gsap are helpful for creating quick animations, but if your JavaScript animations are still running slow, try using the requestAnimationFrame()
method to get them up to speed.
20. Throttle and debounce
Setting limits on how much JavaScript gets executed at once can help fine tune your application's performance. Throttling sets the maximum number of times that a function may be called over time, while debouncing ensures that a function isn't called again until a designated amount of time passes. Medium has a good tutorial for throttling and debouncing JavaScript code.
Tools for improving JavaScript performance
If you need a little extra support, there are some code compression tools designed to help developers optimize their applications' JavaScript performance. Code compression, or code minification, removes unnecessary characters from source code, which results in smaller file sizes and faster load times. Here is a sampling of the the top tools for tuning JavaScript code:
1. Google Closure Compiler
On top of analyzing, parsing and rewriting your code for optimal performance, Google Closure Compiler also double checks your syntax and variable references.
2. YUI Compressor
YUI Compressor is a command-line tool that was created by Yahoo! It delivers a higher compression ratio than most of its competitors. YUI Compressor can also compress CSS files. Different code compression tools may be better suited for certain applications.
3. UglifyJS
UglifyJS is a very popular JavaScript minification tool. It can minify, parse and compress JavaScript code. It can process multiple input files simultaneously, parsing the input files first and then the options. The tool creates a source map file when compressing, which you can use to trace your original code.
Summary
Many of the individual tips above give your JavaScript a very small performance boost; however, if implemented together, you and your users should notice a significant improvement in how fast your websites and applications run. It may be difficult for you to remember all of these pointers when working under a deadline, but the sooner you start implementing best practices, the less likely you are to encounter JavaScript performance problems later on. Therefore, you should keep your own personal list of JavaScript optimization techniques by your side until they become second nature.