Insights

How to improve JavaScript code

Loïc Le Merlus
March 2023

In my role at nuom I get to review a lot of code written by developers of various experience. Below are some tips I often give to developers in order to improve the readability of their code.

Use guards rather than if/else

If statements are one of the most widely used tools in a developer's tool box, allowing you to create logic based on certain conditions. However, junior developers tend to over use them and create code with multiple ifs, which makes the logic difficult to follow. Here is an example:

We check if the user exists, is premium, and that the subscription hasn’t expired, to see if they are allowed to access premium content. But because we have 3 different multiple if/else statements it gets difficult to know which "else" corresponds to which "if", which makes refactoring difficult, especially if we want to add clear error messages.

A better way is to create ''guards''. The principal is to do checks early in a function and return a value immediately if the check fails. This makes the code more readable as the result is closer to the condition. Have a look at the following code, which performs the same function:

We have made the code more readable and we know straight away why "false" is returned in each case, and it becomes clear that "return true" is the happy path for this function.

Use Maps rather than if/else if/switch

In a similar way, 'if/else if' statements are often used to return different values based on a condition. For instance look at the following code:

One way often recommended to improve a code with multiple 'else if', is to use a switch statement, like this:

But there is an even better way, to use a JavaScript Object as a Map:

In this example the Map is hard coded, but using this method we can use data from a database instead, without having to modify the code every time we add a new monster.

Remove duplicates in an array

One operation that is often done is to get a list of unique values in an array. One possibility is to use "Array.reduce" but if all the elements in the array are primitives then there is a better way. Look at the following code, where we want to get all the unique locations of where our employees are based:

Make sure to use at least ES6 for this trick to work.

Shallow and deep copies

While you're creating functionalities for your software, you will probably need to create a copy of an array or a JavaScript object. Let’s look at the following user object:

This user object contains a mix of primitives (strings, numbers, etc..), array, and objects.

Shallow copy

We create a new object/array with the same elements of the original object/array, but not recursively. This means that in a shallow copy, the values of primitives are copied, but for nested object/array it only copies a reference to the array/object.

Let’s illustrate that in an example using the same user:

Deep copy

With a deep copy, the object/array elements are copied recursively, with the references to the original object/array being totally broken.

The JSON method is the one often recommended, but it has 3 big drawbacks:

  • it will throw an error if there is a circular reference in the object
  • functions/classes are not copied
  • in Typescript it looses all the typing information as JSON.parse will return a type of "any"

A better way introduced recently in all browsers and since Node 17 (and now retroactively available in Node 16) is the "structuredClone":

If you want to know more about how to use "structuredClone", then you can find out more here.

You might not need Typescript.

When you ask developers who are using Typescript what's their reason for using it over plain JavaScript, most of them say it’s for static typing. But actually, by that it means that their IDE is able to tell them what parameters are expected in a function, and what are the type of those parameters.

It can be very difficult when you are working on a large code base to know which function is expecting what or how it should be formatted, or what data is returned by a function. For instance, if we call a function to know the last login time of a user, will that function return: a Date, a Timestamp or a String?

Typescript solves this by having static typing that enforces the type of each variable, so that you always know what is expected and where, and it will throw an error if a parameter is the wrong type.

However, you can achieve very similar results without Typescript, by using JSDoc.

JSDoc is a standard for documenting code as comments right alongside the code. It is inspired by similar tools like JavaDoc or phpDocumentor, and is recognised by most modern IDEs.

JSDoc takes the form of multi line comments that you put just before a function or a class, to explain the intent of it, as well as the different parameters, types and return types.

You will recognise them because it always starts with "/**".

For instance, let’s look at the following class:

We have a comment at the top explaining the overall functionality of the class, comments before the constructor explaining which parameters are expected, and you can notice the type of the parameters between the curly brackets "{...}". We do the same things with the methods of the class.

If we then try to create an object of that class, our IDE will let us know what is expected, including the types, even though we are not using Typescript:

You probably noticed that we didn’t add any JSDoc for "getLocation", however, our IDE is still able to help us here and let us know which type is being returned:

Let’s make things slightly more complicated and create a user class:

If we wanted to create a method to calculate the user age, our IDE is able to help us here again:

The IDE knows that "this._dob" is of type "Date" and is suggesting all the right methods for me to use, the same way as it would do with Typescript.

Using JSDoc properly we can know the type of every variable just by hovering over it, and get all the help from the IDE:

So if you just want to improve your developer experience, you can get all the help from your IDE without having to refactor any code. And just by adding helpful comments, there is no need to add extra tooling or compilation steps.

The main difference with Typescript for type checking, is that you do not get an error if you give a variable of the wrong type or omit a parameter.

You can write the following code and not know there is a mistake if you are not paying attention.

So be sure to sanitise your inputs and check your variables as you would normally do.

We can help

These are just some quick tips to help developers. If you have any questions related to the above content, or want to know more about JavaScript, our capabilities, or how we can help across discovery, design, delivery, then please drop us line at hello@nuom.co.uk

No items found.

Let’s talk!

We design and develop human-centred solutions that create positive outcomes for users. Let’s work together!