JavaScript Refactoring — Conditionals
Tags: codequality, webdev, programming, javascript
Published: 2021-01-25 03:22:02

**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/**

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.