This answer details how short-circuiting works in JavaScript, with all the tricks and related topics, such as operator precedence, if you are looking for a quick definition and already understand how a short circuit works, I would recommend checking other answers.
What we (we thought) knew so far:
First, let's test the behavior that we are all familiar with, inside the if() block, where we use && to check if these two things are true :
if (true && true) { console.log('bar'); }
Now your first instinct probably says: "Yes, quite simply, the code executes the instruction if both expr1 and expr2 evaluated as true ."
Well, yes and no. You are technically correct, this is the behavior that you described, but itโs not exactly how the code is evaluated, and we need to delve deeper to fully understand.
How exactly is this && and || interpreted ?:
It's time to look "under the hood of the javascript engine." Let's look at this practical example:
function sanitise(x) { if (isNaN(x)) { return NaN; } return x; } let userinput = 0xFF;
Ok, the result is 260 .. but why? To get the answer, we need to understand how the short circuit assessment works.
By definition of MDN, the && operator in expr1 && expr2 is executed as follows:
If expr1 can be converted to true , returns expr2 ; otherwise returns expr1 .
Thus, this means that in our practical example, const res is evaluated as follows:
- Call
expr1 - sanitise(0xFF) 0xFF is a valid hexadecimal number for 250, otherwise I would return NaNexpr1 returned the "true" value, time to execute expr2 (otherwise I would have stopped, since NaN is false)- Since
userinput is true (number), I can add +5 to it
- "Truth" means that the expression can be judged as true. Here is a list of true and false expressions.
So here we were able to avoid additional if blocks and further checks of isNaN with a simple use of the && operator.
How it really works:
By now, we should at least have an idea of โโhow short-circuit statements work. The universal rule states:
(some falsy expression) && expr converted to a false expression(some truthy expression) || expr (some truthy expression) || expr (some truthy expression) || expr (some truthy expression) || expr will match the true expression
Here are some more examples for a better understanding:
function a() { console.log('a'); return false; } function b() { console.log('b'); return true; } if ( a() && b() ){ console.log('foobar'); }
function a() { console.log('a'); return false; } function b() { console.log('b'); return true; } if ( a() || b() ){ console.log('foobar'); }
Another annoying but very important thing [Operator Priority]:
Ok, hope you get it! The last thing we need to know is the rule of precedence of operators, namely:
- The
&& operator is always executed before || operator.
Consider the following example:
function a() { console.log('a'); return true;} function b() { console.log('b'); return false;} function c() { console.log('c'); return false;} console.log(a() || b() && c());
This will return as possibly confusing to some like a() . The reason is quite simple, itโs just our sight, which deceives us, because we are used to reading from left to right. Let's take console.log() and what not, and focus solely on evaluation
true || false && false
Now, to wrap your head around this:
We said that the && operator takes precedence, so it is rated as the first. Come up with a definition to help us better represent the assessment.
expr1 && expr2
Where to:
expr2 is falseexpr1 true || false true || false true || false true || false
So that was the hard part, now true || false true || false evaluates to true || false true || false ( expr1 is the left side of && ).
- Given
|| operator stops execution if expr1 || expr2 expr1 || expr2 expr1 || expr2 expr1 || expr2 in expr1 evaluates to true, expr1 is executed, and code execution stops.
Return value true
Well ... that was pretty complicated, all because of a few weird rules and semantics. But remember, you can always avoid operator precedence with () - as in math
function a() { console.log('a'); return true;} function b() { console.log('b'); return false;} function c() { console.log('c'); return false;} console.log((a() || b()) && c());