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.