I am an experienced developer, but I just ran into an issue that took me a while to figure out and am looking for an explanation. I was relying on short circuiting and spent more time than I care to admit debugging this. If this belongs on another Stack Exchange site, please advise.
I expect the following to evaluate to false, but it passes:
(false && true || true) => true
It is as though it's being interpreted as this:
((false && true) || true) => true
... but the solution is this:
(false && (true || true)) => false
Why doesn't false
short circuit the operation in the first example? Is there some sort of lookahead I don't know about?
Solution summary: For those who (like me) never knew conditional operators have a similar precedence as mathematical operators - the same concept of implied parenthesis applies:
3 * 2 + 1 => (3 * 2) + 1 => 7
false && true || true => (false && true) || true => true
Logical AND (
&&
) has a higher precedence than logical OR (||
).You could fix the problem by adding the parentheses you used in your example.
However, it would be much more readable to use an explicit
if
statement instead:Here's a reference chart for operator precedence in JS. (See #13 and #14.)
If you're having a hard time understanding precedence, try substituting
*
for&&
and+
for||
:Clearly, by order of operations, the
x * y
will be executed first. So if you want to execute they + z
first instead, you would use parentheses.Short circuiting has nothing to do with the syntax. It's simply a quirk of boolean operators. So don't think of it like
The short circuiting doesn't call some sort of "abort" function that exits the entire operation. It's simply that the boolean operators will ignore their second argument (
b
ina && b
) if it's already ascertainable what the final result will be. (For example,false && (anything)
is always false, so&&
is lazy and doesn't bother evaluating the second argument.)Okay, so applying the precedence rules, we get:
Therefore, the
&&
is evaluated first. Since it's lazy, it sees thefalse
and immediately returnsfalse
:Now it's
||
's turn to evaluate. It sees thefalse
that&&
just returned, and it can't short-circuit becausefalse || true
could still be true. So it has to evaluate thesomethingelse
to get the final result.Therefore, the code
if((false && anything) || somethingelse)
is essentially equivalent toif (somethingelse)
.