Categories
Refactoring

JavaScript Refactoring — Conditionals

Spread the love

We can clean up our JavaScript code so that we can work with them more easily.

In this article, we’ll look at some refactoring ideas that are relevant for cleaning up JavaScript conditionals.

Decompose Conditional

We can break up long conditional expressions into smaller conditional expressions that are named.

For instance, instead of writing:

let ieIEMac = navigator.userAgent.toLowerCase().includes("mac") && navigator.userAgent.toLowerCase().includes("ie")

We write:

let userAgent = navigator.userAgent.toLowerCase();
let isMac = userAgent.includes("mac");
let isIE = userAgent.toLowerCase().includes("ie");
let isMacIE = isMac && isIE;

We broke the conditional expressions by assigning smaller expressions into their own variables and then combining them to make everything easier to read.

Consolidate Conditional Expression

If we have multiple short conditional expressions assigned to their own variables, then we can combine them into one.

For example, instead of writing:

const x = 5;
const bigEnough = x > 5;
const smallEnough = x < 6;
const inRange = bigEnough && smallEnough;

We write:

const x = 5;
const inRange = x > 5 && x < 6;

Since the expressions are so short, even combining them doesn’t make the much longer, so we can do that.

Consolidate Duplicate Conditional Fragments

If we have duplicate expressions or statements in a conditional block, then we can move them out.

For instance, instead of writing:

if (price > 100) {
  //...
  complete();
} else {
  //...
  complete();
}

We write:

if (price > 100) {
  //...
} else {
  //...
}
complete();

This way, we don’t have to do repeatedly call the complete function unnecessarily.

Remove Control Flag

If we have used a control flag for a loop, then we often have loops that look like:

let done = false;
while (!done) {
  if (condition) {
    done = true;
  }
  //...
}

In the code above, done is the control flag and we set done to true so that we stop the while loop when the condition is true .

Instead, we can use break to stop the loop as follows:

let done = false;
while (!done) {
  if (condition) {
    break;
  }
  //...
}

Replace Nested Conditional with Guard Clauses

Nested conditional statements are very hard to read, so instead of using them, we can use guard clauses instead.

For instance, instead of having the following nested conditional statements:

const fn = () => {
  if (foo) {
    if (bar) {
      if (baz) {
        //...
      }
    }
  }
}

We write:

const fn = () => {
  if (!foo) {
    return;
  }

  if (!bar) {
    return;
  }

  if (baz) {
    //...
  }
}

In the code above, the guard clauses are:

if (!foo) {
  return;
}

and:

if (!bar) {
  return;
}

They return the function early if those conditions are false.

With that, we don’t need to have any nesting.

Replace Conditional with Polymorphism

Instead of having a switch statement to that does the same thing to different kinds of data, we can create subclasses for each and then have different methods that are tailored to the type of object that it’s called on.

For instance, instead of writing the following:

class Animal {
  constructor(type) {
    this.type = type;
  }

  getBaseSpeed() {
    return 100;
  }

  getSpeed() {
    switch (this.type) {
      case ('cat'): {
        return getBaseSpeed() * 1.5
      }
      case ('dog'): {
        return getBaseSpeed() * 2
      }
      default: {
        return getBaseSpeed()
      }
    }
  }
}

We write the following:

class Animal {
  constructor(type) {
    this.type = type;
  }

  getBaseSpeed() {
    return 100;
  }
}

class Cat extends Animal {
  getSpeed() {
    return super.getBaseSpeed() * 1.5;
  }
}

class Dog extends Animal {
  getSpeed() {
    return super.getBaseSpeed() * 2;
  }
}

Our switch statement was long and we have to tailor the case blocks to different kinds of objects.

Therefore, it’s better that we turn the switch statement into their own subclass so they can have their own method for getting the speed.

Introduce Null Object

If we have repeated checks for null or undefined , then we can define a subclass that represents the null or undefined version of the class and then use that.

For instance, instead of writing:

class Person {
  //...
}

We write:

class Person {
  //...
}

class NullPerson extends Person {
  //...
}

Then instead of setting properties of the object where Person would be null or undefined , we set it to the NullPerson instance instead.

This eliminates the need to check for those values with conditionals.

Conclusion

We can create a null or undefined version of the class to set it instead of null or undefined .

Also, we can decompose long conditional expressions into smaller ones and combine small ones into big ones.

Leave a Reply

Your email address will not be published.

If you like the content of this blog, subscribe to my email list to get exclusive articles not available to anyone else.