JavaScript best practices to improve code quality
Tags: javascript, codequality, programming, tutorial
Published: 2021-04-19 09:58:30

---
title: JavaScript best practices to improve code quality
published: true
description: JavaScript is constantly evolving and adding new features. Here's a primer on how you can use them to improve your code's quality and maintainability.
tags: javascript, codequality, programming, tutorial
//cover_image: ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tguwme5sq0u5rz0kx1d.png)
canonical_url: https://deepsource.io/blog/javascript-code-quality-best-practices/
---

If you write JavaScript today, it’s worth your time staying in the know of all the updates the language has seen in the past few years. Since 2015, with the release of ES6, a new version of the ECMAScript spec has been released each year. Each iteration adds new features, new syntax, and Quality of Life improvements to the language. JavaScript engines in most browsers and Node.js quickly catch up, and it’s only fair that your code should catch up as well. That’s because with each new iteration of JavaScript comes new idioms and new ways to express your code, and many a time, these changes may make the code more maintainable for you and your collaborators.

Here are some of the latest ECMAScript features, and by induction, JavaScript and Node.js that you can make use of to write cleaner, more concise, and more readable code.

## 1. Block scored declarations

Since the inception of the language, JavaScript developers have used `var` to declare variables. The keyword `var` has its quirks, the most problematic of those being the scope of the variables created by using it.

```javascript
var x = 10
if (true) { var x = 15 // inner declaration overrides declaration in parent scope
 console.log(x) // prints 15
}
console.log(x) // prints 15
```

Since variables defined with `var` are not block-scoped, redefining them in a narrower scope affects the value of the outer scope.

Now we have two new keywords that replace `var`, namely `let` and `const` that do not suffer from this drawback.

```javascript
let y = 10
if (true) { let y = 15 // inner declaration is scoped within the if block
 console.log(y) // prints 15
}
console.log(y) // prints 10
```

`const` and `let` differ in the semantics that variables declared with `const` cannot be reassigned in their scope. This does not mean they are immutable, only that their references cannot be changed.

```javascript
const x = [] x.push("Hello", "World!")
x // ["Hello", "World!"]

x = [] // TypeError: Attempted to assign to readonly property.
```

## 2. Arrow functions

Arrow functions are another very important feature introduced recently to JavaScript. They come bearing many advantages. First and foremost, they make the functional aspects of JavaScript beautiful to look at and simpler to write.

```javascript
let x = [1, 2, 3, 4] x.map(val => val * 2) // [2, 4, 6, 8]
x.filter(val => val % 2 == 0) // [2, 4]
x.reduce((acc, val) => acc + val, 0) // 10
```

In all of the above examples the arrow functions, named after the distinctive arrow `=>`, replace traditional functions with a concise syntax.

1. If the function body is a single expression, the scope brackets `{}` and `return` keyword are implied and need not be written.
2. If the function has a single argument, the argument parentheses `()` are implied and need not be written.
3. If the function body expression is a dictionary, it must be enclosed in parentheses `()`.

Another significant advantage of arrow functions is that they do not define a scope but rather exist within the parent scope. This avoids a lot of pitfalls that can arise with the use of the `this` keyword. Arrow functions have no bindings for `this`. Inside the arrow function, the value of `this` is the same as that in the parent scope. Consequently, arrow functions cannot be used as methods or constructors. Arrow functions don’t work with `apply`, `bind`, or `call` and have no bindings for `super`.

They also have certain other limitations such as lack of the `arguments` object which traditional functions can access and the inability to `yield` from the function body.

Thus arrow functions are not a 1:1 replacement for standard functions but welcome addition to JavaScript’s feature set.

## 3. Optional chaining

Imagine a deeply nested data structure like this `person` object here. Consider you wanted to access the first and last name of this person. You would write this in JavaScript like so:

```javascript
person = { name: { first: 'John', last: 'Doe', }, age: 42
}
person.name.first // 'John'
person.name.last // 'Doe'
```

Now imagine what would happen if the `person` object did not contain a nested `name` object.

```javascript
person = { age: 42
}
person.name.first // TypeError: Cannot read property 'first' of undefined
person.name.last // TypeError: Cannot read property 'last' of undefined
```

To avoid such errors, developers had to resort to code like the following, which is unnecessarily verbose, hard to read, and unpleasant to write — a very bad trio of adjectives.

```javascript
person && person.name && person.name.first // undefined
```

Meet optional chaining, a new feature of JavaScript that does away with this monstrosity. Optional chaining short-circuits the digging process as soon as it encounters a `null` or `undefined` value and returns `undefined` without raising an error.

```javascript
person?.name?.first // undefined
```

The resultant code is much concise and cleaner.

## 4. Null-ish coalescing

Before introducing the null-ish coalescing operator, JavaScript developers used the OR operator `||` to fall back to a default value if the input was absent. This came with a significant caveat that even legitimate but falsy values would result in a fallback to the defaults.

```javascript
function print(val) { return val || 'Missing'
} print(undefined) // 'Missing'
print(null) // 'Missing'

print(0) // 'Missing'
print('') // 'Missing'
print(false) // 'Missing'
print(NaN) // 'Missing'
```

JavaScript has now proposed the null coalescing operator `??`, which offers a better alternative in that it only results in a fallback if the preceding expression is null-ish. Here null-ish refers to values that are `null` or `undefined`.

```javascript
function print(val) { return val ?? 'Missing'
} print(undefined) // 'Missing'
print(null) // 'Missing'

print(0) // 0
print('') // ''
print(false) // false
print(NaN) // NaN
```

This way, you can ensure that if your program accepts falsy values as legitimate inputs, you won’t end up replacing them with fallbacks.

## 5. Logical assignment

Let’s say you want to assign a value to a variable if and only if the value is currently null-ish. A logical way to write this would be like so:

```javascript
if (x === null || x == undefined) { x = y
}
```

If you knew about how short-circuiting works, you might want to replace those 3 lines of code with a more succinct version using the null-ish coalescing operator.

```javascript
x ?? (x = y) // x = y if x is nullish, else no effect
```

Here we use the short-circuiting feature of the null-ish coalescing operator to execute the second part `x = y` if `x` is null-ish. The code is pretty concise, but it still is not very easy to read or understand. The logical null-ish assignment does away with the need for such a workaround.

```javascript
x ??= y // x = y if x is nullish, else no effect
```

Along the same lines, JavaScript also introduces logical AND assignment `&&=` and logical OR assignment `||=` operators. These operators perform assignment only when the specific condition is met and have no effect otherwise.

```javascript
x ||= y // x = y if x is falsy, else no effect
x &&= y // x = y if x is truthy, else no effect
```

**Pro-tip:** If you’ve written Ruby before, you’ve seen the `||=` and `&&=` operators, since Ruby does not have the concept of falsy values.

## 6. Named capture groups

Let’s start with a quick recap of capture groups in regular expressions. A capture group is a part of the string that matches a portion of regex in parentheses.

```javascript
let re = /(\d{4})-(\d{2})-(\d{2})/
let result = re.exec('Pi day this year falls on 2021-03-14!') result[0] // '2020-03-14', the complete match
result[1] // '2020', the first capture group
result[2] // '03', the second capture group
result[3] // '14', the third capture group
```

Regular expressions have also supported named capture groups for quite some time, which is a way for the capture groups to be referenced by a name rather than an index. Now, with ES9, this feature has made its way to JavaScript. Now the result object contains a nested groups object where each capture group’s value is mapped to its name.

```javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
let result = re.exec('Pi day this year falls on 2021-03-14!') result.groups.year // '2020', the group named 'year'
result.groups.month // '03', the group named 'month'
result.groups.day // '14', the group named 'day'
```

The new API works beautifully with another new JavaScript feature, de-structured assignments.

```javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
let result = re.exec('Pi day this year falls on 2021-03-14!')
let { year, month, day } = result.groups year // '2020'
month // '03'
day // '14'
```

## 7. `async` & `await`

One of the powerful aspects of JavaScript is its asynchronicity. This means that many functions that may be long-running or time-consuming can return a Promise and not block execution.

```javascript
const url = 'https://the-one-api.dev/v2/book'
let prom = fetch(url)
prom // Promise {<pending>}

// wait a bit
prom // Promise {<fullfilled>: Response}, if no errors
// or
prom // Promise {<rejected>: Error message}, if any error
```

Here the call to fetch returns a Promise that has the status ‘pending’ when created. Soon, when the API returns the response, it transitions into a ‘fulfilled’ state, and the Response that it wraps can be accessed. In the Promises world, you would do something like this to make an API call and parse the response as JSON.

```javascript
const url = 'https://the-one-api.dev/v2/book'
let prom = fetch(url)
prom // Promise {<fullfilled>: Response}
 .then(res => res.json()) .then(json => console.log(json)) // prints response, if no errors
 .catch(err => console.log(err)) // prints error message, if any error
```

In 2017, JavaScript announced two new keywords `async` and `await`, that make handling and working with Promises easier and more fluent. They are not a replacement for Promises; they are merely syntactic sugar on top of the powerful Promises concepts.

Instead of all the code happening inside a series of ‘then’ functions, `await` makes it all look like synchronous JavaScript. As an added benefit, you can use `try...catch` with `await` instead of handling errors in ‘catch’ functions as you would have to if consuming Promises directly. The same code with `await` would look like this.

```javascript
const url = 'https://the-one-api.dev/v2/book'
let res = await fetch(url) // Promise {<fullfilled>: Response} -await-> Response
try { let json = await res.json() console.log(json) // prints response, if no errors
} catch(err) { console.log(err) // prints error message, if any error
}
```

The `async` keyword is the other side of the same coin, in that it wraps any data to be sent within a Promise. Consider the following asynchronous function for adding several numbers. In the real world, your code would be doing something much more complicated.

```javascript
async function sum(...nums) { return nums.reduce((agg, val) => agg + val, 0)
} sum(1, 2, 3) // Promise {<fulfilled>: 6}
 .then(res => console.log(res) // prints 6

let res = await sum(1, 2, 3) // Promise {<fulfilled>: 6} -await-> 6
console.log(res) // prints 6
```

These new features just the tip of the iceberg. We have barely even scratched the surface. JavaScript is constantly evolving, and new features are added to the language every year. It’s tough to keep up with the constant barrage of new features and idioms introduced to the language manually.

Wouldn’t it be nice if some tool could handle this for us? Fret not, there is. We’ve already talked in detail about [setting up static code analysis in your JavaScript repo](https://deepsource.io/blog/static-analysis-javascript/?utm_source=devto&utm_medium=organic&utm_campaign=contentdistribution&utm_term=jscodequality) using ESLint. It’s extremely useful and should be an indispensable tool of your toolchain. But to be honest, setting up ESLint auto-fix pipelines and processes takes time and effort. Unless you enjoy this sort of plumbing, you’d be better off if you wrote the code and outsourced the plumbing to…DeepSource!

DeepSource can help you with [automating the code reviews](https://deepsource.io/?utm_source=devto&utm_medium=organic&utm_campaign=contentdistribution&utm_term=jscodequality) and save you a ton of time. Just add a `.deepsource.toml` file in the root of the repository and DeepSource will pick it up for scanning right away. The scan will find scope for improvements across your code and help you fix them with helpful descriptions.

[Sign up](https://deepsource.io/signup?utm_source=devto&utm_medium=organic&utm_campaign=contentdistribution&utm_term=jscodequality) and see for yourself!

Originally published on [DeepSource Blog](https://deepsource.io/blog/javascript-code-quality-best-practices/?utm_source=devto&utm_medium=organic&utm_campaign=contentdistribution&utm_term=jscodequality).