135

I would like to know if JavaScript has "short-circuit" evaluation like && Operator in C#. If not, I would like to know if there is a workaround that makes sense to adopt.

duplode
  • 32,686
  • 7
  • 74
  • 141
GibboK
  • 68,054
  • 134
  • 405
  • 638
  • 2
    You're welcome. I've added `https://www.google.com/search?q=site:stackoverflow.com+%s` as a search shortcut (Chrome/Firefox) to speed up searches. – Rob W Sep 23 '12 at 17:39
  • Also here an answer to my question https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators – GibboK Aug 01 '14 at 08:40
  • Further useful resources: [The || evaluation question](https://stackoverflow.com/questions/2100758/javascript-or-variable-assignment-explanation) [The && evaluation question](https://stackoverflow.com/questions/5049006/using-s-short-circuiting-as-an-if-statement#) – Samuel Hulla May 09 '19 at 11:08

3 Answers3

152

Yes, JavaScript has "short-circuit" evaluation.

if (true == true || foo.foo){
    // Passes, no errors because foo isn't defined.
}

Live DEMO

if (false && foo.foo){
    // Passes, no errors because foo isn't defined.
}

Live DEMO

reformed
  • 4,205
  • 10
  • 58
  • 83
gdoron is supporting Monica
  • 142,542
  • 55
  • 282
  • 355
  • 9
    So short circuit it is the standard in JS? – GibboK Sep 23 '12 at 17:42
  • @GibboK. Don't know what you mean by standard, you can't change that behavior, it's the only behavior. (just like with C# by the way) – gdoron is supporting Monica Sep 23 '12 at 17:44
  • 1
    Thanks gdoron, please help me to understand ... in C# I have also binary operator like & so both the operand must be true to pass, instead with && in C – GibboK Sep 23 '12 at 17:46
  • 1
    @GibboK. Then, obviously, there can't be a `Short-circuit` with that logic operator. Just try it yourself. Use my demo. – gdoron is supporting Monica Sep 23 '12 at 17:50
  • 2
    @GibboK: Check out this [operator reference](https://developer.mozilla.org/en-US/learn/javascript). And yes, there is a binary AND operator in JS as well. – Bergi Sep 23 '12 at 17:50
  • And in both javascript and C#, & is a _bitwise_ AND operator. It can return FALSE even if both arguments are true. – Zachary Vance May 29 '14 at 21:17
  • 7
    @GibboK. YES in the standard! But good comment, as in times of JIT-compiling-magic in javascript implementations, one does really want to know if something is "the standard" , or potentially subject to the implementation. The way a condition statement with Binary Logical Operators is evaluated and (short-curcuit) is a standard behavior http://www.ecma-international.org/ecma-262/5.1/#sec-11.11 – humanityANDpeace Aug 04 '15 at 06:11
  • Ancient thread, but actually in C# '&' with boolean operands does non-short-circuit evaluation. With integer operands it does bitwise. – Jasper Kent Aug 31 '17 at 14:14
  • Here is my article that explains short circuits. Hope it can help http://pogsdotnet.blogspot.com/2015/08/javascript-short-circuiting.html – Allan Chua Dec 26 '17 at 15:36
  • I would like to add that the shorter version is very useful in reactjs render and other scenarios, here some examples https://jsfiddle.net/twpvkmfq/1/ (vanilla) – Emeeus May 14 '19 at 00:34
48

This answer goes into great detail on how works in JavaScript, with all the gotcha's and also relevant themes such as operator precedence, if you're looking for a quick definition and already understand how short-circuiting works, I'd recommending checking other answers.


What we (thought we) knew so far:

First let's inspect the behaviour we are all familiar with, inside the if() block, where we use && to check whether the two things are true:

if (true && true) {
   console.log('bar');
} 

Now, your first instinct is probably to say: 'Ah yes, quite simple, the code executes the statement if both expr1 and expr2 are evaluated as true'

Well, yes and no. You are technically correct, that is the behaviour you described, but that's not exactly how the code is evaluated and we'll need to delve deeper in order to fully understand.


How exactly is the && and || interpreted?:

It's time to look "under the hood of the engine". Let's consider this practical example:

function sanitise(x) {
  if (isNaN(x)) {
    return NaN;
  }
  return x;
}

let userinput = 0xFF; // as an example
const res = sanitise(userinput) && userinput + 5

console.log(res);

Well the result is 260.. but why? In order to get the answer, we need to understand how does the short-circuit evaluation work.

By the MDN Definition the && operator in expr1 && expr2 is executed followingly:

If expr1 can be converted to true, returns expr2; else, returns expr1.

So this means, in our practical example, the const res is evaluated the following way:

  1. Invoking expr1 - sanitise(0xFF)
  2. 0xFF is a valid hexadecimal number for 250, otherwise I'd return NaN
  3. The expr1 returned a "truthy" value, time to execute expr2 (otherwise I'd stop as NaN is falsy)
  4. Since userinput is truthy (a number), I can add +5 to it
  • "Truthy" means that expression can be evaluated as true. Here's a list of truthy and falsy expressions.

So here, we were able to avoid additional if blocks and further isNaN checks with a simple usage of the && operator.


How it really works:

By now, we should at least have a picture how the operators work. The universal rule goes:

  • (some falsy expression) && expr will evaluate to falsy expression
  • (some truthy expression) || expr will evaluate to truthy expression

Here are some further examples for better comprehension:

function a() { console.log('a'); return false; }
function b() { console.log('b'); return true; }

if ( a() && b() ){
     console.log('foobar'); 
}

//Evaluates a() as false, stops execution.

function a() { console.log('a'); return false; }
function b() { console.log('b'); return true; }

if ( a() || b() ){
     console.log('foobar'); 
}

/* 1. Evaluates a() as false
   2. So it should execute expr2, which is `b()`
   3. b() returned as true, executing statement `console.log('foobar');`
*/

One last pesky, but very important thing [Operator Precedence]:

Nice, hopefully you're getting the hang of it! Last thing we need to know is a rule about operator precedence, that is:

  • The && operator is always executed prior to the || 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());

// returns a() and stops execution

This will return as, perhaps confusingly to some as a(). Reason is quite simple, it's just our eye-sight that's kind of deceiving us, because we're used to reading left-to-right. Let's take the console.log() and what not out and focus purely on the evaluation

true || false && false

Now to wrap your head around this:

  1. We said the && operator has precedence, so it gets evaluated as first. To help us better imagine the evaluation, think of the definition

    expr1 && expr2
    

    Where:

    • expr2 is false
    • expr1 is true || false
  2. So that was the tricky part, now true || false is evaluated (the expr1 - left-side of the &&).

    • Given the || operator stops execution if expr1 || expr2 in expr1 evaluates as truthy, the expr1 is executed and code execution stops.
  3. The returned value is true

Well.. that was pretty tricky, all because of few weird rules and semantics. But remember, you can always escape operator precedence with the () - just like 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());

/* 1. The () escape && operator precedence
   2. a() is evaluated as false, so expr2 (c()) to be executed
   3. c()  
*/
Samuel Hulla
  • 5,914
  • 6
  • 30
  • 57
  • I would 1) not use the word "compiler". "engine" is more accurate. 2) Not talk about `expr1` and `expr2` or `condition1` or whatever, that is just confusing. Decide for one, you could also introduce local variables, eg. `const expr1 = true; if(expr1 && ...)` – Jonas Wilms May 09 '19 at 10:26
  • @JonasWilms thanks for the input, amended the answer accordingly. – Samuel Hulla May 09 '19 at 10:58
  • 1
    This *still* doesn't directly answer the question asked. – Kevin B May 09 '19 at 15:21
  • 11
    This is the best *"great answer that doesn't explicitly answer the question"* that I've ever seen... – Gerardo Furtado Jun 21 '19 at 09:38
  • 1
    This is the right answer with a deep explanation, should be marked as accepted and upvoted much more than it is currently! – Alexander Kim Mar 29 '20 at 08:06
  • N.B. `0xFF is a valid hexadecimal number for **255**` - you've got 250 there. – mdhansen May 25 '20 at 02:59
  • Brilliant explanation. Only part that's unclear for me is the last example of Operator precedence. – Peter Out Apr 28 '21 at 22:39
  • Regular Inline evaluation of "true || false && false" would evaluate as (true || false) && false. But && takes precedence over ||, so I'd expect the evaluation to instead be true || (false && false). That would return true also, but we know that's not how it works because the console logs indicate that neither b() nor c() runs, only a(). It's just not making sense to me how if expr1 = "true || false" (neither true nor false) & expr2 = false, how can expr1 && expr2 finish evaluation without "waiting" for result of expr1 "true || false"= true. That'll finally give true && false = false. – Peter Out Apr 28 '21 at 22:59
1

The idea is that logical expressions are read left-to-right, and if the value of the left condition is enough to get the total value, the right condition will not be processed and evaluated. Some very simple examples:

function test() {
    const caseNumber = document.querySelector('#sel').value;
    const userChoice = () => confirm('Press OK or Cancel');
    if (caseNumber === '1') {
        console.log (1 === 1 || userChoice());
    } else if (caseNumber === '2') {
        console.log (1 === 2 && userChoice());
    } else if (caseNumber === '3') {
        console.log (1 === 2 || userChoice());
    } else if (caseNumber === '4') {
        console.log (1 === 1 && userChoice());
    } else if (caseNumber === '5') {
        console.log (userChoice() || 1 === 1);
    } else if (caseNumber === '6') {
        console.log (userChoice() && 1 === 2);
    }
}
<label for="sel">Select a number of a test case and press "RUN!":</label>
<br><select id="sel">
  <option value="">Unselected</option>
  <option value="1">Case 1</option>
  <option value="2">Case 2</option>
  <option value="3">Case 3</option>
  <option value="4">Case 4</option>
  <option value="5">Case 5</option>
  <option value="6">Case 6</option>
</select>
<button onclick="test()">RUN!</button>

The first two cases above will print to console results true and false respectively and you will not even see the modal window asking you to press "OK" or "Cancel", because the left condition is sufficient to define the total result. On the contrary, with the cases 3–6, you will see the modal window asking for your choice, because the former two depend on the right part (that is your choice), and the latter two — regardless of the fact that the aggregate values of these expressions do not depend on your choice — because left conditions are read first. So, it is important to place conditions left-to-right based on which ones you want to be processed first.

Roman Karagodin
  • 432
  • 2
  • 6
  • 15