19 Aug 13 Tips to Write Faster, Better-Optimized JavaScript

10 years ago, Amazon shared that every 100ms of latency cost them 1% in sales revenue: across an entire year, 1 second of added load time would cost the company in the region of $1.6 billion. Similarly, Google found that an extra 500ms seconds in search page generation time reduced their traffic by 20%, slicing a fifth off their potential ad revenue.

Few of us may have to deal with such dramatic figures as Amazon and Google, but the same principles apply even on a smaller scale: faster code creates a better user experience and it’s better for business. Especially in web development, speed may be the critical factor thing that gives you an edge on your competitors. Every wasted millisecond on a faster network is amplified on a slow network.

In this article, we’ll look into 13 practical ways that you can increase the speed of your JavaScript code — whether you’re writing server-side code with Node.js or client-side JavaScript. Wherever possible, I’ve included links to benchmark tests created with https://jsperf.com. If you’d like to test these tips for yourself, make sure to click on those links!

Do It Less

“The fastest code is the code that never runs.”

1. Remove Unnecessary Features

It’s easy to jump into optimizing code that’s already been written, but often the biggest performance gains come from taking a step back and asking whether our code needed to be there in the first place.

Before moving on to individual optimisations, ask yourself whether your program needs to do everything that it’s doing. Is that feature, component or function necessary? If not, remove it. This step is incredibly important to improving the speed of your code, but it is easily overlooked!

2. Avoid Unnecessary Steps

On a smaller scale, is every step a function takes necessary to get to the end result? For example, does your data jump through unnecessary hoops in order to get to the end result? The following example may be oversimplified, but it represents something that can be much harder to spot in a larger codebase:

Even in this simple example, the difference in performance is dramatic — running some code is a lot slower than running no code! Though few people would make the mistake above, in longer and more complex code it can be easy to add in unnecessary steps to get to the desired end result. Avoid them!

Do It Less Often

If you can’t remove code, ask yourself if you can do it less often. One of the reasons code is so powerful is that it can allow us to easily repeat actions, but it’s also easy to perform tasks more often than necessary. Here are some specific cases to look out for.

3. Break Out of Loops As Early As Possible

Look out for cases where it’s not necessary to complete every iteration in a loop. For example, if you’re searching for a particular value and find that value, subsequent iterations are unnecessary. You should break terminate the execution of the loop by using a break statement:

Or, if you need to perform actions on only certain elements in a loop, you can skip performing the actions on the other elements using the continuestatement. continue terminates the execution of the statements in the current iteration and immediately moves on to the next one:

The problem with this code is that every time we call whichSideOfTheForce , we create a new object. With every function call, memory is unnecessarily re-allocated to our light and dark arrays.

Given the values in light and dark are static, a better solution would be to declare these variables once and then reference them when calling whichSideOfTheForce . While we could do this by defining our variables in global scope, this would allow them to be tampered with outside of our function. A better solution is to use a closure, and that means returning a function:

Every time we run doSomething , the nested function doSomethingElse is created from scratch. Again, closures provide a solution. If we return a function, doSomethingElse remains private but it will only be created once:

5. Order Code to Minimise the Number of Operations

Often, improvements to code speed can be improved if we think carefully about the order of actions in a function. Let’s imagine we’ve got an array of item prices, stored in cents, and we need a function to sum the items and return the result in dollars:

The key is to make sure that actions are being taken in the best possible order.

6. Learn Big O Notation

Learning about Big O Notation can be one of the best ways to understand why some functions run faster and take up less memory than others — especially at scale. For example, Big O Notation can be used to show, at a glance, why Binary Search is one of the most efficient search algorithms, and why Quicksort tends to be the most performant method for sorting through data.

Do It Faster

The biggest gains in code speed tend to come from the first two categories of optimisation: ‘Do It Less’ and ‘Do It Less Often’. In this section, we’ll look at a few ways to make your code faster that are more concerned with optimising the code you’ve got, rather than reducing it or making it run fewer times.

In reality, of course, even these optimisations involve reducing the size of your code — or making it more compiler-friendly, which reduces the size of the compiler code. But on the surface, you’re changing your code rather than removing it, and that’s why the following are logged under ‘Do It Faster’!

7. Prefer Built-In Methods

For those with experience of compilers and lower-level languages, this point may seem obvious. But as a general rule of them, if JavaScript has a built-in method, use it.

The compiler code is designed with performance optimisations specific to the method or object type. Plus, the underlying language is C++. Unless your use-case is extremely specific, the chance of your own JavaScript implementation outperforming existing methods is very low!

To test this, let’s create our own JavaScript implementation of the Array.prototype.map method:

In my tests, using our new JavaScript map function was roughly 65% slower than using Array.prototype.map . To view the source code of V8’s implementation of Array.prototype.map , click here. And to run these tests for yourself, check out the benchmark.

8. Use the Best Object for the Job

Similarly, the best possible performance also comes from choosing the most appropriate built-in object for the job at hand. JavaScript’s built-in objects go well-beyond the fundamental types: Numbers , Strings , Functions , Objects and so on. Used in the right context, many of these less common objects can offer significant performance advantages.

Get to know the built-in object types and try always to use the best object for your needs, as this can often lead to faster code.

9. Don’t Forget About Memory

As a high-level language, JavaScript takes care of a lot of lower-level details for you. One such detail is memory management. JavaScript uses a system known as garbage collection to free up memory that — as far as it is possible to tell without the explicit instructions from a developer — is no longer needed.

Though memory management is automatic in JavaScript, that doesn’t mean that it’s perfect. There are additional steps you can take to manage memory and reduce the chance of memory leaks.

For example, Sets and Maps also have ‘weak’ variants, known as WeakSetsand WeakMaps . These hold ‘weak’ references to objects. These are not enumerable, but they prevent memory leaks by making sure unreferenced values get garbage collected.

You can also have greater control over memory allocation by using JavaScript’s TypedArray objects, introduced in ES2017. For example, an Int8Array can take values between -128 and 127 , and has a size of just one byte. It’s worth noting, however, that the performance gains of using TypedArrays may be very small: comparing a regular array and a Uint32Array shows a minor improvement in write performance but little or no improvement in read performance (credits to Chris Khoo for these two tests).

10. Use Monomorphic Forms Where Possible

If we set const a = 2 , then the variable a can be considered polymorphic (it can be changed). By contrast, if we were to use 2 directly, that can be considered monomorphic (its value is fixed).

Of course, setting variables is extremely useful if we need to use them multiple times. But if you only use a variable once, it’s slightly faster to avoid setting a variable at all. Take a simple multiplication function:

That’s a pretty small win. But across a large codebase, many small wins like this can add up.

Similarly, using arguments in functions provides flexibility at the expense of performance. Again, arguments are an integral part of programming. But if you don’t need them, you’ll gain a performance advantage by not using them. So, an even faster version of our multiply function would look like this:

As above, the performance improvement is small (in my tests, roughly 2%). But if this kind of improvement could be made many times across a large codebase, it’s worth considering. As a rule, only introduce arguments when a value has to be dynamic and only introduce variables when they’re going to be used more than once.

11. Avoid the ‘Delete’ Keyword

The delete keyword is used to remove an entry from an object. You may feel that it is necessary for your application, but if you can avoid using it, do. Behind the scenes, delete removes the benefits of the hidden class pattern in the V8 Javascript engine, making it a generic slow object, which — you guessed it — performs slower!

Depending on your needs, it may be sufficient simply to set the unwanted property as undefined:

Do It Later

If you can’t do it less, do it less often or do it faster, then there’s a fourth category of optimisation you can use make your code feel faster — even if takes exactly the same amount of time to run. This involves restructuring your code in such a way that less integral or more demanding tasks don’t block the most important stuff.

12. Use Asynchronous Code to Prevent Thread Blocking

By default, JavaScript is single-threaded and runs its code synchronously, one-step-at-a-time. (Under the hood, browser code may be running multiple threads to capture events and trigger handlers, but — as far as writing JavaScript code is concerned — it’s single-threaded).

This works well for most JavaScript code, but if we have events likely to take a long time, we don’t want to block or delay the execution of more important code.

The solution is to use asynchronous code. This is mandatory for certain built-in methods like fetch() or XMLHttpRequest() , but it’s also worth noting that any synchronous function can be made asynchronous: if you have a time-consuming (synchronous) operation, such as performing operations on every item in a large array, this code can be made asynchronous so that it doesn’t block the execution of other code. If you’re new to asynchronous JavaScript, check out my article, A Guide to JavaScript Promises.

In addition, many modules like Node.js’s filesystem have asynchronous and synchronous variants of some of their functions, such as fs.writeFile()and fs.writeFileSync() . In normal circumstances, stick to the default asynchronous method.

13. Use Code Splitting

If you’re using JavaScript on the client-side, your priorities should be making sure that the visuals appear as quickly as possible. A key benchmark is ‘first contentful paint’, which measures the time from navigation to the time when the browser renders the first bit of content from the DOM.

One of the best ways to improve this is through JavaScript code-splitting. Instead of serving your JavaScript code in one large bundle, consider splitting it into smaller chunks, so that the minimum necessary JavaScript code is required upfront. How you go about code splitting will vary depending on whether you’re using React, Angular, Vue or vanilla Javascript.

A related tactic is tree-shaking, which is a form of dead code elimination specifically focused on removing unused or unnecessary dependencies from your codebase. To find out more about this, I recommend this article from Google. (And remember to minify your code for production!)

Conclusion

The best way to ensure you’re actually making useful optimisation to your code is to test them. Throughout this article, I’ve provided code benchmarks using https://jsperf.com/, but you could also check smaller sections of code using:

As for checking the performance of entire web applications, a great starting point is the network and performance section of Chrome’s Dev Tools. I also recommend Google’s Lighthouse extension.

Finally, though important, speed isn’t the be-all and end-all of good code. Readability and maintainability are extremely important too, and there’s rarely a good reason to make minor speed improvements if that leads to more time spent finding and fixing bugs.

If you’re a newer developer, I hope this opened your eyes to some of the performance-boosting techniques at your disposal. And if you’re more experienced, I hope this article was a useful refresher.

Got any performance tips that I’ve missed? Let me know in the comments!