JavaScript Refactoring Tips — Reducing Function Complexity

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. However, it’s hard to write a piece of clean JavaScript code.

In this article, we’ll look at ways to reduce the complexity of our JavaScript functions.

Move Repeated Code into a Single Location

We should move repeated code into a single location so that we can change them easily if needed as we only need to change one instead of multiple pieces of code.

For instance, instead of writing:

const button = document.querySelector('button');
let toggled = true;
button.addEventListener('click', () => {
  toggled = !toggled;
  if (toggled) {
    document.querySelector("p").style.color = 'red';
  } else {
    document.querySelector("p").style.color = 'green';
  }
})
Enter fullscreen mode Exit fullscreen mode

where we have 2 instances of document.querySelector(“p”) , we should write:

const button = document.querySelector('button');
const p = document.querySelector("p");
let toggled = true;
button.addEventListener('click', () => {
  toggled = !toggled;
  if (toggled) {
    p.style.color = 'red';
  } else {
    p.style.color = 'green';
  }
})
Enter fullscreen mode Exit fullscreen mode

As we can see, it’s shorter and we only have to change the selector once if we decide that we don’t want to select the p element anymore.

Another common instance of code is magic numbers, where we have code with numbers that we don’t know the meaning of from the code itself.

For instance, we have code that looks like:

let x = 1;
let y = 1;
let z = 1;
Enter fullscreen mode Exit fullscreen mode

Where we have 3 1’s that we don’t know the meaning of. We can remove the repetition and make the numbers self-documenting by writing:

const numPeople = 1;
let x = numPeople;
let y = numPeople;
let z = numPeople;
Enter fullscreen mode Exit fullscreen mode

Then we know that 1 is the value of numPeople . Now we know that 1 means the number of people.

Simplify Functions

Functions should be as simple as possible. Therefore, they should only do one thing and not have too many lines of code in them. They shouldn’t have more than 30 lines of code.

The old cases that we use JavaScript functions shouldn’t be used. For instance, we shouldn’t use IIFEs for modules or blocks. Also, we shouldn’t define constructor functions anymore.

Instead, we should use the class syntax, where we can include multiple instance methods of the class inside the class.

This reduces the need for long functions significantly.

Also, functions should be pure whenever we can define them. This means they shouldn’t commit side effects.

For instance, the best simple functions are functions like:

const add = (a, b) => a + b;
Enter fullscreen mode Exit fullscreen mode

The function above doesn’t have any side effects, as it doesn’t modify any variables outside the function. Also, it’s pure because it always returns the same outputs for the same inputs.

It also only does one thing, which is adding numbers. Therefore, the cognitive load on the reader of the code isn’t going to be too taxed since it only does one thing and nothing else.

Use Guard Clauses Instead of Nested If Statements

Guard clauses are where we end the function when the function encounters some condition.

It replaces nested if blocks that are long and hard to read.

For instance, instead of writing the following function:

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

We write:

const greet = (firstName, lastName, greeting) => {
  if (typeof firstName !== 'string') {
    throw new Error('first name is required');
  }
  if (typeof lastName !== 'string') {
    throw new Error('last name is required');
  }
  if (typeof greeting !== 'string') {
    throw new Error('greeting is required');
  }
  return `${greeting}, ${firstName}${lastName}`;
}
Enter fullscreen mode Exit fullscreen mode

In the 2nd example, we eliminated the nested if statements by throwing errors if each argument isn’t a string.

This reduced the nested if statements to no nested if statements while doing the same thing.

Nesting is hard to read and understand, and we should get rid of them everywhere.

Conclusion

Repeated code is always bad. We should always remember the Do not Repeat Yourself (DRY) principle.

Also, a lot of things that previously used traditional functions for shouldn’t be used anymore. Many functions can be replaced with modules, blocks, classes, and arrow functions.

Finally, nested if statements should be replaced with guard clauses since they can do the same checks as the nested if statements without having hard to read nested if statements.

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