JavaScript Refactoring Tips — Making Functions Clearer and Cleaner

John Au-Yeung - Jan 25 '21 - - Dev Community

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/

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at how to make our functions clearer.

Using Destructuring for Object Parameters

If we want our function to take lots of arguments, then we should take one big object argument.

To do that, we should use the destructuring syntax to destructure the properties of the object we pass in as the argument as variables.

For instance, instead of writing:

const greet = (obj) => {
  return `${obj.greeting}, ${obj.firstName}${obj.lastName}`;
}
Enter fullscreen mode Exit fullscreen mode

We should write:

const greet = ({
  greeting,
  firstName,
  lastName
}) => {
  return `${greeting}, ${firstName}${lastName}`;
}
Enter fullscreen mode Exit fullscreen mode

This way, we have a lot less repetition, and it makes everyone clear what properties the object has in the arguments.

It’s like having multiple arguments without actually having to pass in multiple arguments.

It’s a lot more flexible as the order of properties in an object doesn’t matter as with multiple parameters, but we still can access each property individually as variables.

Naming Our Callback Functions

Naming things makes reading code easier. This is the same with callback functions. For instance, instead of writing:

const arr = [1, 2, 3].map(a => a * 2);
Enter fullscreen mode Exit fullscreen mode

We should instead write:

const double = a => a * 2;
const arr = [1, 2, 3].map(double);
Enter fullscreen mode Exit fullscreen mode

Now we know that our callback function actually is used for doubling each entry of the original array.

Make Our Conditionals Descriptive

Conditionals can be made more descriptive by writing the conditional expressions in a conditional statement in their own function.

For instance, instead of writing:

if (score === 100 ||
  remainingPlayers === 1 ||
  remainingPlayers === 0) {
  quitGame();
}
Enter fullscreen mode Exit fullscreen mode

We should write:

const winnerExists = () => {
  return score === 100 ||
    remainingPlayers === 1 ||
    remainingPlayers === 0
}

if (winnerExists()) {
  quitGame();
}
Enter fullscreen mode Exit fullscreen mode

This way, we know that those conditions are conditions for checking if a winner exists in our game code.

In the first example, we have a long-expression inside the parentheses of the if that most people probably don’t know that it’s checking.

But in the 2nd example, once we put it in a named function, we know that the conditions are actually checking for.

Having a named function in our conditionals is much clearer than just having a bunch of boolean expressions together.

Replacing Switch Statements with Maps or Objects

switch statements are long and error-prone because of its length. Therefore, we should replace them with shorter pieces of code if we can.

Many switch statements can be replaced with maps or objects. For instance, if we have the following switch statement:

const getValue = (prop) => {
  switch (prop) {
    case 'a': {
      return 1;
    }
    case 'b': {
      return 2;
    }
    case 'c': {
      return 3;
    }
  }
}
const val = getValue('a');
Enter fullscreen mode Exit fullscreen mode

We can replace that with an object or map as follows:

const obj = {
  a: 1,
  b: 2,
  c: 3
}
const val = obj['a'];
Enter fullscreen mode Exit fullscreen mode

As we can see, the switch was long. We need to nest multiple blocks with multiple return statements just to get a value returned given a prop value.

With an object, we have an object obj :

const obj = {
  a: 1,
  b: 2,
  c: 3
}
Enter fullscreen mode Exit fullscreen mode

which has 3 properties with string keys that we can return a value from if we access it with the bracket notation as we did above. With the bracket notation, the keys don’t have to be valid identifiers. Instead, they can be any string.

We can also replace the object with a map as follows:

const map = new Map([['a', 1], ['b', 2], ['c', 3]])
const val = map.get('a')
Enter fullscreen mode Exit fullscreen mode

As we can see, with maps, the code is also a lot shorter. We defined the Map with one line by passing in an array with entries that consists of arrays of key and value in that order.

Then we just use the Map instance’s get method to get the value from the key.

One benefit that maps have over objects is that we can have other values like numbers, booleans, or objects as keys. Whereas objects can only have strings or symbols as keys.

Also, we don’t have to worry about inherited properties with maps.

Conclusion

Object parameters can be made clearer and shorter with the destructuring syntax. This way, the properties can be selectively accessed as variables.

Conditionals can be made more descriptive by putting the conditional expressions in its own named function. Likewise, we should name our callback functions to make reading the code easier.

Finally, switch statements should be replaced with maps and objects as much as possible.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .