Javascript. 6 tips to improve the performance of the code you write
List of tips that are easy to implement on a day-to-day basis to help you write more optimized Javascript code
If you’ve made it this far, I’m sure you already have some practice developing with Javascript. As you may have seen, it is a very open language: Javascript imposes very few restrictions on working with it. That is why I think that from time to time it is very interesting to stop, take a breath, and review some very easy-to-follow recommendations to write much more optimized code.
I have compiled some of the techniques that I use the most in this article. I hope you find them useful and of course, I will be happy to read yours in the comments. As I always say, it is about continuing to learn together.
Let’s go there!
Compute only once
Many times we do not pay attention to the times that we force Javascript to reserve memory for the same object or to perform the same heavy calculation:
function calculateSomethingHeavy() {
...
}function foo(bar) {
const result = calculateSomethingHeavy();
// do something with result and bar
}foo(1);
foo(2);
Here for example every time we invoke the foo function we are invoking the calculateSomethingHeavy
function although it does not depend on the arguments.
Another simpler example that illustrates this case well is:
function foo(bar) {
const object = { key: 'value'};
// do something with bar
}foo(1);
Here every time we invoke foo
we are creating the same object
over and over again, with the consequent expenditure of memory.
The solution to this problem is to use “closures” that allow us to remember those values so that we are not forced to recalculate them every time:
function calculateSomethingHeavy() {
...
}function fooCreator() {
const result = calculateSomethingHeavy();
return foo(bar) {
// do something with result and bar
}
}const foo = fooCreator();foo(1);
foo(2);
Or even use the modulo pattern:
const module = (function foo(bar) {
const object = { key: 'value'};
// do something with bar
return {
doSomething: function(bar) {
// do something with bar
}
}
})();module.doSomething(1);
Use native methods
Many times we tend to write functions that do things that Javascript itself already has implemented in the form of native functions of the language.
The most common case is usually that of performing operations on an array of elements to which we apply a function: you are still surprised to see the following piece of code but I promise you that it is very common:
function applyFunctionToArray(arr, fn) {
const newArray = [];
for (let i = 0; i < arr.length; i ++) {
const result = fn(arr[i]);
newArray.push(result);
}
return newArray;
}
If you pay attention, you will realize that it is a very generic implementation of the Array.prototype.map
method that allows us to apply a function to the elements of an array and generate a new one with the results.
That piece of code, which, as I said, is not that difficult to find on the way, has a much worse performance than working with map
directly. Publicizing this type of functions that Javascript implements natively is one of the reasons why every Wednesday I share a “javascriptera recipe”: we often reinvent the wheel without knowing that Javascript already gives us what we want.
Avoid “delete”
We can use the delete
word to delete a key from an object. However, it has a counterpart: when we use it, the V8 engine begins to treat that object as if it were a “flat” object, eliminating a series of optimizations that it performs underneath.
Therefore, the recommendation is that whenever we can assign undefined
to the key that we want to eliminate so that the performance of the object in memory is not affected:
const obj = { 'foo': 1, 'bar': 2, 'zeta': 3};
obj.foo = undefined;
The other alternative is to use the “rest” operator to eliminate the key that we do not want, however this form is slower than delete itself, so it is not worth it:
const obj = { 'foo': 1, 'bar': 2, 'zeta': 3};
const {foo, ...objectWithoutFoo} = obj;
Split your code
When we create web applications, one of the most important things we have to pay attention to is getting the code to load as quickly as possible so that the user can see “something” as quickly as possible.
This is greatly influenced by the size of the JS files that the browser has to download and how optimized the application code is.
Thanks to the magic of Webpack and other code “packers” it is very easy to divide the application into different pieces that the browser requires as the user browses the web without having to download “the entire application”.
But the benefits of Webpack do not end there, it is also capable of performing an operation called “tree shaking” to eliminate dependencies that we are not using and that we often import without realizing it:
My advice is that you familiarize yourself with these concepts because they help to have much lighter and faster applications.
Not everything has to be flat objects
For some years now, Javascript has had a series of special objects that are optimized for certain types of tasks.
For example, “Sets” allow us to have arrays without duplicates and Maps and Weak Maps offer interesting benefits when working with key-value objects:
That is why I want to encourage you from time to time to dig a little deeper into these types of objects that Javascript provides natively, since many times they will allow us to optimize certain pieces of our code.
To show a button. Thanks to the “Sets” eliminating duplicates from an array is as simple as this:
const arr = [1, 2, 1, 2, 3, 4];
const arrayWithoutDuplicates = [...new Set(arr)];
And to finish … take care of the libraries / packages you use
There is a series of libraries that are usually installed at the beginning of projects and that nevertheless tend to be quite “heavy”, which causes the final files of our application to reach a large size.
Examples of these types of libraries are Lodash or MomentJS. In the case of the first one, many times we bring it to perform operations that Javascript can already perform natively.
In the case of the latter, its creators recommend using much more optimized alternatives, such as one of my recent discoveries: the Date-FNS library.
Final thoughts
As you can see, all these tips are very easy to implement in our applications and following the saying that “everything adds up” will help us to have much more optimized and faster applications.
Also, if you are curious to compare how fast different implementations of the same logic work, you can consult the JSBench.me tool: