Related
The following works:
let x = 1 && console.log("true"); (-- logs true)
let y = 0 && console.log("true"); (-- logs nothing)
The above shows that the statement before && operator is acting like an expression.
Then I tried this:
console.log(let m = 5); // Error
What is going on here? If that's an expression then why it didn't work in this case and if it's not an expression then why it worked in the first two cases?
The following works -
let x = 1 && console.log("true"); (-- logs true)
let y = 0 && console.log("true"); (-- logs nothing)
The above shows that the statement before && operator is acting like an expression
This is where you are mistaken. The && operator is used to join expressions, not statements (even though it has control flow effects). This parses as follows:
let x = (1 && console.log("true")); (-- logs true)
let y = (0 && console.log("true")); (-- logs nothing)
console.log is acting as an expression here, not let .... Function calls always return a value (which may be undefined). If you logged x and y, you'd see that x === undefined (result of 1 && undefined) and y === 0 (result of 0 && <not evaluated>).
It is probably confusing you here that && short-circuits: In the first expression, the first operand of && is 1 (truthy), so the second operand - the expression which is a call to console.log - has to be evaluated; in the second expression, the first operand of && is the falsy 0, so && short-circuits to 0, not evaluating (not calling) console.log("true").
let statements are statements and not expressions, which is why you get a syntax error in your second example.
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.
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
The answer is yes!
This answer goes into great detail on how short-circuiting works in JavaScript, with all the gotchas 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 recommend checking other answers.
What we (thought we) knew so far:
First let’s inspect the behavior we are all familiar with, inside the if condition, where we use && to check whether the two expressions are true:
const expr1 = true;
const expr2 = true;
if (expr1 && expr2) {
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 behavior 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 javascript engine.
Let’s consider this practical example:
function sanitize(x) {
if (isNaN(x)) {
return NaN;
}
return x;
}
let userInput = 0xFF; // As an example.
const res = sanitize(userInput) && userInput + 5
console.log(res);
Well, the result is 260, but why?
In order to get the answer, we need to understand how the short-circuit evaluation works.
By the MDN definition, the && operator in expr1 && expr2 performs as follows:
Logical AND (&&) evaluates operands from left to right, returning immediately with the value of the first falsy operand it encounters; if all values are truthy, the value of the last operand is returned.
If a value can be converted to true, the value is so-called truthy.
If a value can be converted to false, the value is so-called falsy.
[…]
As each operand is converted to a boolean, if the result of one conversion is found to be false, the AND operator stops and returns the original value of that falsy operand; it does not evaluate any of the remaining operands.
Or, more simply, in an older revision of the documentation:
Operator
Syntax
Description
Logical AND (&&)
expr1 && expr2
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:
Invoke expr1, which is sanitize(userInput), or sanitize(0xFF).
Run sanitize(0xFF): check isNaN(x), or isNaN(0xFF), (which results in false because 0xFF is a valid hexadecimal number literal for 255), return x which is 0xFF, or 255. If isNaN(x) was true, sanitize would’ve returned NaN.
The expr1 resulted in 255, a “truthy” value, so it’s time to evaluate expr2. If NaN was returned, it’d stop as NaN is falsy.
Since sanitize(userInput) is truthy (a non-zero, finite number), keep going and add 5 to userInput.
“Truthy” means that expression can be evaluated as true.
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 short-circuit operators work.
The operators that exhibit short-circuiting behavior are:
Logical AND (&&)
Logical OR (||)
Nullish coalescing operator (??)
Optional chaining (?.)
The universal rule goes:
a && b && … && z evaluates to the first falsy operand, or to the last operand if none is found to be falsy.
a || b || … || z evaluates to the first truthy operand, or to the last operand if none is found to be truthy.
a ?? b ?? … ?? z evaluates to the first operand which is neither null nor undefined, or to the last operand, otherwise.
a?.b?.…?.z accesses each property in the chain and evaluates to the value of the nested z property if all previous links of the chain evaluate to something that can be converted to an object (i.e. neither null nor undefined); otherwise it returns undefined, regardless of which nested property fails.
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");
}
// `a()` is evaluated as `false`; execution is stopped.
function a() {
console.log("a");
return false;
}
function b() {
console.log("b");
return true;
}
if (a() || b()){
console.log("foobar");
}
/*
** 1. `a()` is evaluated as `false`.
** 2. So it should evaluate `expr2`, which is `b()`.
** 3. `b()` is evaluated `true`.
** 4. The statement `console.log("foobar");` is executed.
*/
function a() {
console.log("a");
return null;
}
function b() {
console.log("b");
return false;
}
function c() {
console.log("c");
return true;
}
if (a() ?? b() ?? c()){
console.log("foobar");
}
/*
** 1. `a()` is evaluated as `null`.
** 2. So it should evaluate `expr2`, which is `b()`.
** 3. `b()` is evaluated as `false`; execution is stopped.
*/
const deeply = {
get nested(){
console.log("nested");
return {
get object(){
console.log("object");
return null;
}
};
}
};
if (deeply?.nested?.object?.but?.not?.that?.deep){
console.log("foobar");
}
/*
** 1. `deeply` is evaluated as an object.
** 2. `deeply?.nested` is evaluated as an object.
** 3. `deeply?.nested?.object` is evaluated as `null`.
** 4. `?.but?.not?.that?.deep` is essentially skipped over; the entire optional chain is evaluated as `undefined`; execution is stopped.
*/
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 evaluated 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());
// "a" is logged; execution is stopped.
The expression a() || b() && c() will, perhaps confusingly to some, result in a().
The reason is quite simple, it’s just our eyesight 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
&& having a higher precedence than || means that the operands closest to && are evaluated first — but after all operations with an even higher precedence already being evaluated.
Since there’re only && and || here, these are just false and false.
|| works the same way, including the rule that all operations with an even higher precedence should already be evaluated.
So first, we need to evaluate false && false, which is just false.
Then we evaluate true || false (with the resulting false), which is true.
The whole expression is equivalent to (true || (false && false)), which is (true || (false)), which is (true).
You can also try an alternative perspective: in the ECMAScript specification (which is what the JavaScript language is based on), the expression expr1 || expr2 follows a pattern called the LogicalORExpression.
Following its definition, in simple terms, both expr1 and expr2 are their own LogicalANDExpression.
If you wanted to evaluate something like true || false && false, you’d have to evaluate the LogicalORExpression: (true) || (false && false).
You know that (true) is just true, but you don’t immediately know what (false && false) is.
So then, you’d have to evaluate the LogicalANDExpression: (false) && (false).
And now you’re done, because (false) is just false.
Only once you know the result of evaluating each LogicalANDExpression, you can move on to evaluate the constituting LogicalORExpression.
This is exactly what is meant by && being evaluated before ||, or && having a higher precedence than ||.
(NB: These grammar rules kill two birds with one stone: they define the evaluation order (via recursive definitions) and the operator precedence (in this case: left to right, and && before ||).)
Well, that might seem pretty tricky, all because of a few weird rules and semantics.
But remember, you can always escape operator precedence with parentheses, also known as the grouping operator (…) — 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 parenthesized part is evaluated first.
** 2. `a()` is evaluated as `true`, so `b()` is skipped
** 3. `c()` is evaluated as `false`, stops execution.
*/
And we have yet to talk about where to place the ?? operator in terms of precedence!
But don’t worry: since operator precedence rules between && and || and ?? would be too confusing and too complicated, it is actually not allowed to put them next to each other!
They can only appear together in the same expression if it’s very clear which one is evaluated first.
(a ?? b) && c // Clearly, `(a ?? b)` is evaluated first.
a ?? (b && c) // Clearly, `(b && c)` is evaluated first.
a ?? b && c // Unclear! Throws a SyntaxError.
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.
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.
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
The answer is yes!
This answer goes into great detail on how short-circuiting works in JavaScript, with all the gotchas 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 recommend checking other answers.
What we (thought we) knew so far:
First let’s inspect the behavior we are all familiar with, inside the if condition, where we use && to check whether the two expressions are true:
const expr1 = true;
const expr2 = true;
if (expr1 && expr2) {
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 behavior 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 javascript engine.
Let’s consider this practical example:
function sanitize(x) {
if (isNaN(x)) {
return NaN;
}
return x;
}
let userInput = 0xFF; // As an example.
const res = sanitize(userInput) && userInput + 5
console.log(res);
Well, the result is 260, but why?
In order to get the answer, we need to understand how the short-circuit evaluation works.
By the MDN definition, the && operator in expr1 && expr2 performs as follows:
Logical AND (&&) evaluates operands from left to right, returning immediately with the value of the first falsy operand it encounters; if all values are truthy, the value of the last operand is returned.
If a value can be converted to true, the value is so-called truthy.
If a value can be converted to false, the value is so-called falsy.
[…]
As each operand is converted to a boolean, if the result of one conversion is found to be false, the AND operator stops and returns the original value of that falsy operand; it does not evaluate any of the remaining operands.
Or, more simply, in an older revision of the documentation:
Operator
Syntax
Description
Logical AND (&&)
expr1 && expr2
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:
Invoke expr1, which is sanitize(userInput), or sanitize(0xFF).
Run sanitize(0xFF): check isNaN(x), or isNaN(0xFF), (which results in false because 0xFF is a valid hexadecimal number literal for 255), return x which is 0xFF, or 255. If isNaN(x) was true, sanitize would’ve returned NaN.
The expr1 resulted in 255, a “truthy” value, so it’s time to evaluate expr2. If NaN was returned, it’d stop as NaN is falsy.
Since sanitize(userInput) is truthy (a non-zero, finite number), keep going and add 5 to userInput.
“Truthy” means that expression can be evaluated as true.
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 short-circuit operators work.
The operators that exhibit short-circuiting behavior are:
Logical AND (&&)
Logical OR (||)
Nullish coalescing operator (??)
Optional chaining (?.)
The universal rule goes:
a && b && … && z evaluates to the first falsy operand, or to the last operand if none is found to be falsy.
a || b || … || z evaluates to the first truthy operand, or to the last operand if none is found to be truthy.
a ?? b ?? … ?? z evaluates to the first operand which is neither null nor undefined, or to the last operand, otherwise.
a?.b?.…?.z accesses each property in the chain and evaluates to the value of the nested z property if all previous links of the chain evaluate to something that can be converted to an object (i.e. neither null nor undefined); otherwise it returns undefined, regardless of which nested property fails.
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");
}
// `a()` is evaluated as `false`; execution is stopped.
function a() {
console.log("a");
return false;
}
function b() {
console.log("b");
return true;
}
if (a() || b()){
console.log("foobar");
}
/*
** 1. `a()` is evaluated as `false`.
** 2. So it should evaluate `expr2`, which is `b()`.
** 3. `b()` is evaluated `true`.
** 4. The statement `console.log("foobar");` is executed.
*/
function a() {
console.log("a");
return null;
}
function b() {
console.log("b");
return false;
}
function c() {
console.log("c");
return true;
}
if (a() ?? b() ?? c()){
console.log("foobar");
}
/*
** 1. `a()` is evaluated as `null`.
** 2. So it should evaluate `expr2`, which is `b()`.
** 3. `b()` is evaluated as `false`; execution is stopped.
*/
const deeply = {
get nested(){
console.log("nested");
return {
get object(){
console.log("object");
return null;
}
};
}
};
if (deeply?.nested?.object?.but?.not?.that?.deep){
console.log("foobar");
}
/*
** 1. `deeply` is evaluated as an object.
** 2. `deeply?.nested` is evaluated as an object.
** 3. `deeply?.nested?.object` is evaluated as `null`.
** 4. `?.but?.not?.that?.deep` is essentially skipped over; the entire optional chain is evaluated as `undefined`; execution is stopped.
*/
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 evaluated 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());
// "a" is logged; execution is stopped.
The expression a() || b() && c() will, perhaps confusingly to some, result in a().
The reason is quite simple, it’s just our eyesight 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
&& having a higher precedence than || means that the operands closest to && are evaluated first — but after all operations with an even higher precedence already being evaluated.
Since there’re only && and || here, these are just false and false.
|| works the same way, including the rule that all operations with an even higher precedence should already be evaluated.
So first, we need to evaluate false && false, which is just false.
Then we evaluate true || false (with the resulting false), which is true.
The whole expression is equivalent to (true || (false && false)), which is (true || (false)), which is (true).
You can also try an alternative perspective: in the ECMAScript specification (which is what the JavaScript language is based on), the expression expr1 || expr2 follows a pattern called the LogicalORExpression.
Following its definition, in simple terms, both expr1 and expr2 are their own LogicalANDExpression.
If you wanted to evaluate something like true || false && false, you’d have to evaluate the LogicalORExpression: (true) || (false && false).
You know that (true) is just true, but you don’t immediately know what (false && false) is.
So then, you’d have to evaluate the LogicalANDExpression: (false) && (false).
And now you’re done, because (false) is just false.
Only once you know the result of evaluating each LogicalANDExpression, you can move on to evaluate the constituting LogicalORExpression.
This is exactly what is meant by && being evaluated before ||, or && having a higher precedence than ||.
(NB: These grammar rules kill two birds with one stone: they define the evaluation order (via recursive definitions) and the operator precedence (in this case: left to right, and && before ||).)
Well, that might seem pretty tricky, all because of a few weird rules and semantics.
But remember, you can always escape operator precedence with parentheses, also known as the grouping operator (…) — 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 parenthesized part is evaluated first.
** 2. `a()` is evaluated as `true`, so `b()` is skipped
** 3. `c()` is evaluated as `false`, stops execution.
*/
And we have yet to talk about where to place the ?? operator in terms of precedence!
But don’t worry: since operator precedence rules between && and || and ?? would be too confusing and too complicated, it is actually not allowed to put them next to each other!
They can only appear together in the same expression if it’s very clear which one is evaluated first.
(a ?? b) && c // Clearly, `(a ?? b)` is evaluated first.
a ?? (b && c) // Clearly, `(b && c)` is evaluated first.
a ?? b && c // Unclear! Throws a SyntaxError.
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.
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.
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
The answer is yes!
This answer goes into great detail on how short-circuiting works in JavaScript, with all the gotchas 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 recommend checking other answers.
What we (thought we) knew so far:
First let’s inspect the behavior we are all familiar with, inside the if condition, where we use && to check whether the two expressions are true:
const expr1 = true;
const expr2 = true;
if (expr1 && expr2) {
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 behavior 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 javascript engine.
Let’s consider this practical example:
function sanitize(x) {
if (isNaN(x)) {
return NaN;
}
return x;
}
let userInput = 0xFF; // As an example.
const res = sanitize(userInput) && userInput + 5
console.log(res);
Well, the result is 260, but why?
In order to get the answer, we need to understand how the short-circuit evaluation works.
By the MDN definition, the && operator in expr1 && expr2 performs as follows:
Logical AND (&&) evaluates operands from left to right, returning immediately with the value of the first falsy operand it encounters; if all values are truthy, the value of the last operand is returned.
If a value can be converted to true, the value is so-called truthy.
If a value can be converted to false, the value is so-called falsy.
[…]
As each operand is converted to a boolean, if the result of one conversion is found to be false, the AND operator stops and returns the original value of that falsy operand; it does not evaluate any of the remaining operands.
Or, more simply, in an older revision of the documentation:
Operator
Syntax
Description
Logical AND (&&)
expr1 && expr2
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:
Invoke expr1, which is sanitize(userInput), or sanitize(0xFF).
Run sanitize(0xFF): check isNaN(x), or isNaN(0xFF), (which results in false because 0xFF is a valid hexadecimal number literal for 255), return x which is 0xFF, or 255. If isNaN(x) was true, sanitize would’ve returned NaN.
The expr1 resulted in 255, a “truthy” value, so it’s time to evaluate expr2. If NaN was returned, it’d stop as NaN is falsy.
Since sanitize(userInput) is truthy (a non-zero, finite number), keep going and add 5 to userInput.
“Truthy” means that expression can be evaluated as true.
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 short-circuit operators work.
The operators that exhibit short-circuiting behavior are:
Logical AND (&&)
Logical OR (||)
Nullish coalescing operator (??)
Optional chaining (?.)
The universal rule goes:
a && b && … && z evaluates to the first falsy operand, or to the last operand if none is found to be falsy.
a || b || … || z evaluates to the first truthy operand, or to the last operand if none is found to be truthy.
a ?? b ?? … ?? z evaluates to the first operand which is neither null nor undefined, or to the last operand, otherwise.
a?.b?.…?.z accesses each property in the chain and evaluates to the value of the nested z property if all previous links of the chain evaluate to something that can be converted to an object (i.e. neither null nor undefined); otherwise it returns undefined, regardless of which nested property fails.
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");
}
// `a()` is evaluated as `false`; execution is stopped.
function a() {
console.log("a");
return false;
}
function b() {
console.log("b");
return true;
}
if (a() || b()){
console.log("foobar");
}
/*
** 1. `a()` is evaluated as `false`.
** 2. So it should evaluate `expr2`, which is `b()`.
** 3. `b()` is evaluated `true`.
** 4. The statement `console.log("foobar");` is executed.
*/
function a() {
console.log("a");
return null;
}
function b() {
console.log("b");
return false;
}
function c() {
console.log("c");
return true;
}
if (a() ?? b() ?? c()){
console.log("foobar");
}
/*
** 1. `a()` is evaluated as `null`.
** 2. So it should evaluate `expr2`, which is `b()`.
** 3. `b()` is evaluated as `false`; execution is stopped.
*/
const deeply = {
get nested(){
console.log("nested");
return {
get object(){
console.log("object");
return null;
}
};
}
};
if (deeply?.nested?.object?.but?.not?.that?.deep){
console.log("foobar");
}
/*
** 1. `deeply` is evaluated as an object.
** 2. `deeply?.nested` is evaluated as an object.
** 3. `deeply?.nested?.object` is evaluated as `null`.
** 4. `?.but?.not?.that?.deep` is essentially skipped over; the entire optional chain is evaluated as `undefined`; execution is stopped.
*/
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 evaluated 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());
// "a" is logged; execution is stopped.
The expression a() || b() && c() will, perhaps confusingly to some, result in a().
The reason is quite simple, it’s just our eyesight 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
&& having a higher precedence than || means that the operands closest to && are evaluated first — but after all operations with an even higher precedence already being evaluated.
Since there’re only && and || here, these are just false and false.
|| works the same way, including the rule that all operations with an even higher precedence should already be evaluated.
So first, we need to evaluate false && false, which is just false.
Then we evaluate true || false (with the resulting false), which is true.
The whole expression is equivalent to (true || (false && false)), which is (true || (false)), which is (true).
You can also try an alternative perspective: in the ECMAScript specification (which is what the JavaScript language is based on), the expression expr1 || expr2 follows a pattern called the LogicalORExpression.
Following its definition, in simple terms, both expr1 and expr2 are their own LogicalANDExpression.
If you wanted to evaluate something like true || false && false, you’d have to evaluate the LogicalORExpression: (true) || (false && false).
You know that (true) is just true, but you don’t immediately know what (false && false) is.
So then, you’d have to evaluate the LogicalANDExpression: (false) && (false).
And now you’re done, because (false) is just false.
Only once you know the result of evaluating each LogicalANDExpression, you can move on to evaluate the constituting LogicalORExpression.
This is exactly what is meant by && being evaluated before ||, or && having a higher precedence than ||.
(NB: These grammar rules kill two birds with one stone: they define the evaluation order (via recursive definitions) and the operator precedence (in this case: left to right, and && before ||).)
Well, that might seem pretty tricky, all because of a few weird rules and semantics.
But remember, you can always escape operator precedence with parentheses, also known as the grouping operator (…) — 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 parenthesized part is evaluated first.
** 2. `a()` is evaluated as `true`, so `b()` is skipped
** 3. `c()` is evaluated as `false`, stops execution.
*/
And we have yet to talk about where to place the ?? operator in terms of precedence!
But don’t worry: since operator precedence rules between && and || and ?? would be too confusing and too complicated, it is actually not allowed to put them next to each other!
They can only appear together in the same expression if it’s very clear which one is evaluated first.
(a ?? b) && c // Clearly, `(a ?? b)` is evaluated first.
a ?? (b && c) // Clearly, `(b && c)` is evaluated first.
a ?? b && c // Unclear! Throws a SyntaxError.
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.
I'm working with this JS plugin, and I've encountered some syntax I've never seen before. I understand what it's doing, but I'm not sure why it works.
Here's an example of one instance of it:
settings.maxId != null && (params.max_id = settings.maxId);
Is this just taking advantage of conditionals and the single = ? Is this common syntax for JS?
In JavaScript the = operator is an expression and evaluates the assigned value. Because it is an expression it can be used anywhere an expression is allowed even though it causes a side-effect.
Thus:
settings.maxId != null && (params.max_id = settings.maxId)
Means: If settings.maxId is not null then (and only then, since && is short circuiting) evaluate the right-expression (params.max_id = settings.maxId) which in turn causes the value of settings.maxId to be assigned to params.max_id.
This is much more clearly written as:
if (settings.maxId != null) {
params.max_id = settings.maxId
}
Happy coding.
The && operator is known as "boolean AND". Typically, you'd see it in an if statement:
if (x == true && y == false) {
but that's not a restriction. You may use it in any valid expression to "combine" the boolean values of its operands into a single boolean result, according to the logical "AND" operation:
var z = (x == true && y == false);
// z is now true or false, accordingly
One of the lovely things about && is that it "short circuits". In false && true, because the first operand is false the entire expression may only evaluate to false, so the second operand is not even evaluated.
Let's check that again:
var z = (false && foo());
// z is now false
In this statement, the function foo is never even called! It doesn't have to be, for the program to know that z will be false.
This is more than an optimisation — you can rely on it.
Some silly people use this technique to rewrite conditional statements:
if (x == 0) {
foo();
}
into hard-to-read single expressions:
(x == 0) && foo();
Now, consider that assignment can be an expression just like a function call:
var a = (b = c);
Or:
var a = (b = foo());
And add in a conditional via the above technique:
var a = ((x == 0) && (b = foo()));
Now the entire expression b = foo() won't be evaluated at all if x is not 0, because of short circuiting.
We don't even need to do anything with the result of the && operation, and if we don't store it to a you're left with just:
(x == 0) && (b = foo());
which is a statement that'll assign b to the value of foo() only if x is 0.
Avoid it. It's hard to read. Just use an if statement.
this statement will assign params.max_id = settings.maxId only if settings.maxId != null due to the fact that && is a short-circuit logic operator
this behaviour is due to the fact that javascript will evaluate the condition until it's necessary. thus, if first condition is false and the second is in AND there's no need to check further