DEV Community

Michael Lee πŸ•
Michael Lee πŸ•

Posted on

Why is it you can execute a function on the right side of an AND && operator in JavaScript

I always assumed that operators like && and || are for comparing true and false statements on both sides.

But I'm seeing a pattern in React where, on the right side is a function and the left is a truthy statement that will then allow the right to be executed.

Example:

truthy statement && function
Enter fullscreen mode Exit fullscreen mode

I understand that JavaScript will check this from left to right, so left being true will then allow it to check the right. But my assumption is the right should expect a true or false statement, but in this case it's just a function.

I'm so used to seeing the && operator in this context:

if (true && true) {
  // Execute this
}
Enter fullscreen mode Exit fullscreen mode

That this:

true && execute this
Enter fullscreen mode Exit fullscreen mode

Works at all.

I guess my assumption is that JavaScript will just evaluate anything next to an operator whether it returns a true or false.

Top comments (4)

Collapse
 
peerreynders profile image
peerreynders • Edited

Being an operator it's part of an expression:

"An expression is any valid unit of code that resolves to a value."

Logical AND and Logical OR are binary operators, i.e. they require two operands. Those operands can be expressions because an expression resolves to a value. A function invocation is considered an expression because it will resolve to a value after the call (even if it is just undefined).

On top of this && and || use short circuit evaluation:

  • Given lhs && rhs the rhs expression will never be evaluated if lhs evaluates to a falsy value. That falsy value is what the && operator evaluates to.
  • Similarly with lhs || rhs the rhs expression will never be evaluated if lhs evaluates to a truthy value. That truthy value is what the || operator evaluates to.

So somethingFalsy && fn(value) won't execute fn(value) while somethingTruthy && fn(value) will execute fn(value). In the former case somethingFalsy will be the resulting value (ignored if not used) while whatever fn(value) returns is the result for the latter.

Collapse
 
moopet profile image
Ben Sinclair

If you're out shopping for something to nibble on, and you have $1 left in your pocket, and you put something in your basket that costs $1, does it make sense to keep adding more things?

Why not?

Because you know that you've already reached the limit, and adding more isn't going to work.

That's how expressions work:

let n = 10;

if (n > 5 && n > 6 && n > 11 && n > 20 && n > 0) {
  console.log("hello there");
}
Enter fullscreen mode Exit fullscreen mode

When this code is run by the Javascript engine, it'll get to each part of the expression in turn, left-to-right, and test it. We know n > 5 is correct, and true, and we also know n > 6 is true. However, when we reach n > 11 we can throw out the whole thing because the expression as a whole can no longer be true. It doesn't matter what parts of the expression come afterwards, it can never be true.

The parser does this as it goes, basically to save effort. It can run your code faster if it doesn't have to do a lot of working-things-out that it knows are pointless. You might think that testing n > 20 is going to take so little time that it doesn't make any difference, and you'd be right... but remember, expressions can include all sorts of things:

if (n > 20 && weatherSimulation.process('Europe', 2022).renderMap('Berlin').hasClouds()) {
Enter fullscreen mode Exit fullscreen mode

And in this case, you don't want your supercomputer to use 1.21 gigawatts working out the weather forecast in ludicrous detail when you know it's not needed because you don't have sufficient ns to use it anyway.

The second thing you need to know is that this:

"hello there!";
Enter fullscreen mode Exit fullscreen mode

is as valid a thing as this:

myCoolFunction();
Enter fullscreen mode Exit fullscreen mode

i.e. you can have bare expressions in code and they're perfectly fine. They don't necessarily do anything, but they're not invalid.

So to put it together:

n > 20 && console.log("n is greater than 20);
Enter fullscreen mode Exit fullscreen mode

will work out the expression, just as if it was inside an if, from left to right, until it's sure it's true or certain it's false, and it'll do as little work as it can to find that out. The parser doesn't care that the console.log here isn't returning anything useful for the expression, it doesn't know any different. It's processing it exactly as it processed the n > 20 part.

You don't often see this in Javascript. I'd go so far as to say it's not good style.
To borrow from Python, explicit is better than implicit, sparse is better than dense, and readability counts. Any of these could be written as a clear if which would be a lot easier to extend in the future.

Collapse
 
darkwiiplayer profile image
π’ŽWii πŸ³οΈβ€βš§οΈ

While it is true that short-circuit evaluation serves to avoid evaluating expressions unnecessarily, treating them as boolean operators hides an important fact: They aren't commutative.

With a purely boolean AND operator, you could start evaluating either side of the expression and stop if it was false.

However, in JavaScript, this isn't the case, as even when the RHS evaluates to false, the LHS is still relevant, because it could be any of several falsey values, or a truthy value, in which case the operation would evaluate to its RHS.

This becomes more apparent when you rewrite an expression like this:

a && b == a ? b : a
a || b == a ? a : b
Enter fullscreen mode Exit fullscreen mode

With many truly commutative operations, it is up to the language to define a clear order of evaluation.

For example, what would you expect let foo = 1 ; String(foo++)+String(foo++) to evaluate to? Normally one would expect 12, but depending on the language, such things may be clearly defined, or up to the implementation.

In C++, for example, the order is unspecified, so such an expression could return both "12" or "21", depending on what order the compiler decides to do things in.

Strictly specifying left to right evaluation, in this case, is a language feature, not an optimisation (and may, at times, actually hinder optimisation)

Collapse
 
michael profile image
Michael Lee πŸ•

This totally looks like it could be by design of JavaScript.

262.ecma-international.org/12.0/#s...

Where in that:

The value produced by a && or || operator is not necessarily of type Boolean. The value produced will always be the value of one of the two operand expressions.