DEV Community

Alex MacArthur
Alex MacArthur

Posted on • Originally published at macarthur.me on

Streamlining Conditional Statements with Logical Operators

I've been seeing my preference change in how I write simple conditional statements in JavaScript. Consider the following:

if (snack) {
  eat(snack);
}
Enter fullscreen mode Exit fullscreen mode

Back in the day, that's how just about every "if I have this, then do this" statement looked. As complexity and context changed, I might have pivoted a bit, but for the most part, this was the go-to pattern. And for good reason -- it's clear and easy to translate into spoken vernacular:

If I have a snack, then eat it.
Enter fullscreen mode Exit fullscreen mode

Early Return Gets the Worm

Then, I started to shift toward preferring the early return:

if (!snack) {
  return;
}

cat(snack);
Enter fullscreen mode Exit fullscreen mode

Not having to nest the meat of my code in an if/else block felt simpler. Do a quick check, and if you're not qualified to be here, don't bother running anything else. Just get outta the way.

My hunch is that once fluency with the semantics of the language became stronger, my brain naturally began to style the code in light of how it's read as code, rather than spoken as English. And for whatever reason, the flow of an early return was cognitively easier to grasp, particularly as the complexity of the method potentially grew.

This becomes clearer with a more complex example. Something like this is totally fine:

Before Early Returns
let greeting;

if (isFamily(person)) {
  greeting = "hug";
} else if (isBuddy(person)){
  greeting = "high five";
} else {
  greeting = "handshake";
}

return greeting;
Enter fullscreen mode Exit fullscreen mode

But it feels stringy and a little more difficult to read than something like this:

After Early Returns
if (isFamily(person)) {
  return "hug":
} 

if (isBuddy(person)){
  return "high five";
}

return "handshake";
Enter fullscreen mode Exit fullscreen mode

What's interesting here is that while it's easier to read as code, it's not at all how people speak. As the semantics become more second nature, the oral flow of the code seems to become less of a concern.

Along Came Short-Circuiting

Soon enough, my preference changed again. This time, toward leveraging logical operators for simple expressions.

After executing one side of the && or || operators, JavaScript will short-circuit if it's logically unnecessary to run the remaining expression(s), returning the value of the last expression that was evaluated. You've probably seen short-circuiting used with the || operator when setting fallback values for variables.

const myVar = "left side" || "right side"
// evaluates to "left side"

const myOtherVar = null || "not null at all";
// evaulates to "not null at all"
Enter fullscreen mode Exit fullscreen mode

This tactic is cleaner than using a ternary, and far more elegant than an if/else block.

Good: If/Then Block
let myVar; 

if (otherVal) {
  myVar = otherVal;
} else {
  myVar = "fallback";
}
Enter fullscreen mode Exit fullscreen mode
Better: Ternary
let myVar = otherVal ? otherVal : "fallback";
Enter fullscreen mode Exit fullscreen mode
Best: Logical Operator
let myVar = otherVal || "fallback";
Enter fullscreen mode Exit fullscreen mode

Similarly, the && operator continues to evaluate as long as the previous value is truthy, returning the last evaluated expression.

const myVar = "left side" && "right side"
// evaluates to "right side"

const func = () => "a string"
const myVar = "" && func()
// evaluates to ""
Enter fullscreen mode Exit fullscreen mode

A Simple Short-Circuited Conditional

And that makes for some succinct conditional statements, allowing you to abandon the if/else block altogether. As long as the first expression is truthy, the next will be evaluated as well.

Before: If/Then Block
if (snack) {
  eat(snack);
}
Enter fullscreen mode Exit fullscreen mode
After: Logical Operator
snack && eat(snack);
Enter fullscreen mode Exit fullscreen mode

A Slightly More Intense Example

For something a little more involved, let's say you wanted to attempt a chain of actions only until one is successful, storing that value in a variable. And if none is successful, fall back to a default value. It's possible to pull this off using the same sort of if/else block, dealing with the stringy nature of the flow.

Option #1: If/Else Block
let firstTruthyReturnValue;

if (tryIt(var1)) {
  firstTruthyReturnValue = tryIt(var1);
} else if (tryIt(var2)) {
  firstTruthyReturnValue = tryIt(var2);
} else if (tryIt(var3)) {
  firstTruthyReturnValue = tryIt(var3);
} else {
  firstTruthyReturnValue = "default value";
}
Enter fullscreen mode Exit fullscreen mode

Or, for a more modern approach, you could use Array.prototype.find() to find that value. It's a bit more elegant, but requires that you also handle the default value a bit more explicitly than you might have hoped.

Option #2: Array.prototype.find()
const possibilities = [
  val1, 
  val2, 
  val3
];

let firstTruthyReturnValue = possibilities.find(val => {
  return tryIt(val)
});

firstTruthyReturnValue = firstTruthyReturnValue === undefined ? "default" : firstTruthyReturnValue;
Enter fullscreen mode Exit fullscreen mode

But by using a logical operator, all that mess can be pulled together more elegantly, while preserving the ability set a default value.

Option #3: Logical Operators
let firstTruthyReturnValue = 
  tryIt(var1) || 
  tryIt(var2) || 
  tryIt(var3) || 
  "default value";
Enter fullscreen mode Exit fullscreen mode

Possible Objections

There may be some purists out there who insist on strictly using the if/else block, switch statement, and ternary for their conditionals. That's fine -- I'm only documenting my personal progression of preference to date.

There are also those who probably say this approach makes the code less readable. I empathize with that. It takes a second to get your brain to reliably parse conditionals written in this way especially when it's so far removed from how people speak.

But that's not a deterrent for me, maybe for the same reason many favor the early return, or even those who are good with using the || operator to set fallback values for variables. Once you get used to the semantics, the gained elegance might hook you for life.

Or, you might yet again change your preference a few months down the road, which is entirely possible for me.

                (This is an article published at macarthur.me. [Read it online here](https://macarthur.me/posts/streamlining-conditional-statements-with-logical-operators).)
Enter fullscreen mode Exit fullscreen mode

Latest comments (0)