JavaScript Best Practice — Replacing Old with New
Tags: codequality, webdev, programming, javascript
Published: 2021-01-27 00:01:15

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, and we can follow some best practices to improve our code.

Since ES6 was introduced, new constructs are replacing older ones for good reasons. It’s much shorter, cleaner, and easier to understand.

In this article, we’ll look at which older constructs that can be replaced with new ones, including replacing `then` with `async` and `await`, replacing dictionary objects with `Map`s, replacing `apply` with the spread operator, and replacing function constructors with the class syntax.

### Replace Then with Async / Await

When we chain promises, we used to do it by using the `then` method and then returning another promise in the callback function that we pass into `then`.

This means that we have code that looks something like this:

```
Promise.resolve(1)
  .then((val) => {
    console.log(val);
    return Promise.resolve(2);
  })
  .then((val) => {
    console.log(val);
    return Promise.resolve(3);
  })
  .then((val) => {
    console.log(val);
  })
```

With the introduction of the `async` and `await` syntax, which is just a shorthand for calling the `then` method repeatedly. We can write the code above like this:

```
(async () => {
  const val1 = await Promise.resolve(1);
  console.log(val1);
  const val2 = await Promise.resolve(2);
  console.log(val2);
  const val3 = await Promise.resolve(3);
  console.log(val3);
})();
```

They both output 1, 2 and 3, but the second code snippet is so much shorter. It’s so much clearer that there’s no reason to go back to the old syntax.

Also, we can loop through promises and run them one after the other by using the `for-await-of` loop. We can loop through the promises we have above by rewriting the code like the following:

```
(async () => {
  const promises = [
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ]

  for await (let p of promises) {
    const val = await p;
    console.log(val);
  }
})();
```

Code like the one we have above is very handy for looping through many promises or promises that are created on the fly, which we can’t do in the earlier examples.

### Replacing Dictionary Objects with Maps

ES6 also introduced the `Map` constructor, which lets us create hash maps or dictionaries without using JavaScript objects with string keys to do so.

`Map`s are also better because they have their own methods to get and manipulate the entries.

For example, instead of writing:

```
const dict = {
  'foo': 1,
  'bar': 2
}
```

We write:

```
const dict = new Map([
  ['foo', 1],
  ['bar', 2]
])
```

Then we can get an entry by using the `get` method as follows:

```
console.log(dict.get('foo'));
```

We can set the value of an existing entry by the key of the entry with the `set` method:

```
dict.set('foo', 2);
```

Also, we can check if an entry exists with the given key with the `has` method:

```
dict.has('baz');
```

There are also the `keys` and `entries` methods to get all the keys of the map and all the entries respectively.

For example, we can write:

```
console.log(dict.keys());
```

To get an iterator object with the keys of the `Map` . This means that we can loop through them with the `for...of` loop or convert it to an array with the spread operator.

Similarly, the `entries` method returns an iterator object with all the entries in the `Map` with each entry being an array of `[key, value]` .

There’s also the `value` method to get an iterator object with all the values in the `Map` .

We can also use other primitive values as keys. If we use objects, we can’t get the value back when we look them up since the lookup is done with the `===` operator which returns `false` is 2 objects that don’t have the same reference even if they have the same content.

These methods aren’t available in a regular object. Also, we might accidentally get or modify the object’s prototype’s properties instead of its own if we use the `for...in` loop.

Therefore, there aren’t many reasons to use a regular object as a dictionary anymore.

![](https://cdn-images-1.medium.com/max/800/0*obmkxAe9AD2xZAxd)Photo by [David Clode](https://unsplash.com/@davidclode?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

### Replace Apply with Spread

If we don’t want to change the value of `this` inside the function, there isn’t much reason to use the `apply` method in functions.

If we only want to call a function with many arguments, we can use the spread operator when passing in an array for our arguments as follows:

```
const arr = [1, 2, 3, 4, 5];
const add = (...args) => args.reduce((a, b) => a + b, 0);
console.log(add(...arr));
```

All we have to do is to use the spread operator on our array, which is the 3 dots operator in the last line, and then the array will be separated into a comma-separated list of arguments.

### Replace Constructor Functions with Classes

Another great ES6 feature is the class syntax for constructor functions. It’s simply syntactic sugar that makes the constructor function looks like a class.

The advantage of it is that it makes inheritance easy.

For example, if we want to inherit from a constructor function, we have to write something like this:

```
function Person(name) {
  this.name = name;
}

function Employee(name, employeeCode) {
  Person.call(this, name);
  Employee.prototype.constructor = Person;
  this.employeeCode = employeeCode;
}

const employee = new Employee('Joe', 1);
console.log(employee)
```

This syntax looks strange coming from class-based object-oriented languages like Java.

However, the class syntax makes things look a lot familiar to developers that used other languages more than JavaScript. We can rewrite the code above to the following:

```
class Person {
  constructor(name) {
    this.name = name;
  }
}

class Employee extends Person {
  constructor(name, employeecode) {
    super(name);
    this.employeecode = employeecode;
  }
}

const employee = new Employee('Joe', 1);
console.log(employee)
```

The code does the same thing as what we have above. However, it’s clearer what we’re inheriting from since we have the `extends` keyword to indicate what we’re inheriting from.

With the constructor function, we have to worry about the value of `this` in the first argument of the `call` method, and what we pass into the subsequent arguments of `call`.

With the class syntax, we don’t have to worry about this. If we forgot to make the `super` call like the following code:

```
class Person {
  constructor(name) {
    this.name = name;
  }
}

class Employee extends Person {
  constructor(name, employeecode) {
    this.employeecode = employeecode;
  }
}

const employee = new Employee('Joe', 1);
console.log(employee)
```

We’ll get the error ‘Uncaught ReferenceError: Must call `super` constructor in derived class before accessing ‘this’ or returning from derived constructor.’

It’s one less chance of making a mistake.

We don’t get any error if we omit the `Person.call` like in the `Employee` constructor function since the browser doesn’t know we want `Employee` to inherit from `Person` .

In addition, when we log the prototype of `employee` , we get that the prototype of `employee` is `Person` as expected with the class syntax.

However, we don’t get that unless we put `Employee.prototype.constructor = Person;` which is easily forgotten.

### Conclusion

`async` and `await` and `for-await-of` are new constructs that make chaining promises much cleaner. It’s much better to use them instead of using `then` because of it.

`for-await-of` also lets us loop through promises that are generated dynamically which we can’t do with `then` or `async` and `await` alone.

`Map` s are much better than plain objects for hashes and dictionaries because it has its own methods to manipulate and get the entries. Also, we may accidentally be accessing the properties of prototype of plain objects.

If we don’t want to change the value of `this` inside a function, we can replace the `apply` method for calling functions with an array of arguments with the spread operator, which does the same thing.

Finally, the class syntax for constructors is much better than the original function syntax since we can inherit from parent classes easier than setting the prototype constructor of a constructor function.