🎩The Magic of || and && in JavaScript

Randall - Jul 16 '22 - - Dev Community

In JavaScript, it's common to see code like this:

const color = options.color || 'red';
Enter fullscreen mode Exit fullscreen mode

Or like this:

const name = data && data.name;
Enter fullscreen mode Exit fullscreen mode

You might know what this code does, but maybe you don't know why. A couple of simple but very clever core language mechanics enable us to write code like this. Despite the clickbait title, there is (sadly) no magic involved. Let's take a look at what's really going on here.

In case you don't know what this code does: the former snippet provides a default value for the color variable. If options.color is a truthy value (such as a non-empty string), then color gets set to that value. But if options.color is a falsy value, such as undefined, then color gets set to the default value of 'red'.

The latter snippet guards against data being undefined (or null). If data is undefined, then trying to reference data.name directly will result in an error being thrown. But if we use the && trick, there will be no error in such a case. name will just be set to undefined.

Boolean Expressions and Short-Circuit Evaluation

To understand how this works, it's important to first understand short-circuit evaluation.

In a language like JavaScript, short-circuit evaluation means that when a JavaScript engine evaluates a boolean expression (such as a || b), it will only evaluate as much of the expression as it needs to, in order to find the answer.

In the expression a || b (meaning a OR b), think logically about what happens if a is true. If a is true, then it doesn't even matter what b is, right? b could be true, false, or anything else, and the result of a || b will be true regardless. If a is true, then a OR b is also true, period.

JavaScript engines are smart enough to know this too, and in practice it means that if a is true (or any other truthy value), the engine won't even bother looking at b.

To see this in action, you could try running the following code:

console.log(true || null.abc);
Enter fullscreen mode Exit fullscreen mode

While it's illegal to try to reference a property on null, this code does not error, because the engine never evaluates the right-hand-side of the expression! It doesn't need to, because the left-hand-side is sufficient to answer the question on its own.

Boolean Operators Don't Return Boolean Values!

The second piece of the puzzle is what's returned by boolean operators, such as || and &&. Often, developers assume that these operators return boolean values, but that's actually not the case.

Try running this code:

console.log('a' || 'b');
Enter fullscreen mode Exit fullscreen mode

It does NOT return true. It returns 'a'!

And this code:

console.log('a' && 'b');
Enter fullscreen mode Exit fullscreen mode

returns 'b'!

What's going on here? This is the part that I find really clever. Boolean expressions do not return boolean values, they actually return the last thing that was evaluated.

Thanks to short-circuit evaluation, when evaluating the expression 'a' || 'b', the engine only evaluates the left-hand-side of the expression. That means that 'a' is the last thing that it evaluates in this expression. Therefore, the return value of the expression is 'a'.

In the expression 'a' && 'b', the left-hand-side of the expression is truthy, but because this is an AND operation, the engine still has to look at the right-hand-side to check if it's truthy or not. If it is, the expression is true. If it's not, then the expression is false. That means in this case, the final thing that's evaluated in this expression is 'b'. That's why 'b' is the return value of the expression.

Putting it Together

That's the theory, but we can boil it down into simpler-sounding rules that might make it easier to use in practice:

  1. The || operator returns the thing on left-hand-side, if that thing is truthy. If it's not, it returns the thing on the right-hand-side.
  2. The && operator returns the thing on the left-hand-side, if that thing is falsy. If it's not, it returns the thing on the right-hand-side.

Let's look at the examples from the top of the article again:

const color = options.color || 'red';
Enter fullscreen mode Exit fullscreen mode

Applying rule #1, if options.color is truthy, the expression returns options.color. Otherwise, it returns 'red'.

const name = data && data.name;
Enter fullscreen mode Exit fullscreen mode

Applying rule #2, if data is falsy, the expression returns data. Otherwise, it returns data.name.

Getting Fancy

You can get pretty fancy with this. For example, if you want to look at a property deep inside of an object, and provide a default value if it's not there, you could write code like this:

const name = (data && data.profile && data.profile.name) || 'Name Unknown';
Enter fullscreen mode Exit fullscreen mode

If data is truthy, it will return the thing on the right. The thing on the right is yet another boolean expression, where if data.profile is truthy, it too will return the thing on the right. If any of this is falsy, then (data && data.profile && data.profile.name) will return the last thing it evaluated. Since that thing would be falsy, || will return the thing on the right-hand-side, 'Name Unknown'.

Does that sound a bit convoluted? I hope it does, and I wouldn't recommend writing code like this. It can get pretty hard to understand. But if you do understand it, then congratulations, you understand the magic of JavaScript boolean expressions.

Optional Chaining and Nullish-Coalescing

As an aside, while code like the above used to be somewhat common in JavaScript, the new-ish optional chaining and nullish-coalescing features give us a simpler way to do the same thing:

const name = data?.profile?.name ?? 'Name Unknown';
Enter fullscreen mode Exit fullscreen mode

We won't dive into that, but it deserves mention.

Conclusion

JavaScript engines only evaluate the parts of a boolean expression that they need to, and boolean expressions return the last thing that was evaluated. That's it. It's a simple mechanism, but it enables some pretty magical-looking constructs. Hopefully this article helped unwind the mystery, and if you have any questions or comments please post them below!

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