JavaScript Draft - Operators, Equality Comparison
JavaScript Draft - Operators, Equality Comparison
org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#a
ssignment_operators
Operators 3
Logical operators 25
|| (OR) 26
OR “||” finds the first truthy value 26
Bitwise OR (|) Operator 27
Syntax 27
Description 27
Using bitwise OR 28
&& (AND) 28
AND “&&” finds the first falsy value 29
Precedence of AND && is higher than OR || 29
Don’t replace if with || or && 29
! (NOT) 30
Truthy / Falsy 30
Tasks 31
What's the result of OR? 31
What's the result of OR'ed alerts? 31
What is the result of AND? 31
What is the result of AND'ed alerts? 32
The result of OR AND OR 32
Check the range between 32
Check the range outside 32
A question about "if" 32
Check the login 33
● An operator is unary if it has a single operand. For example, the unary negation -
reverses the sign of a number:
let x = 1;
x = -x;
alert( x ); // -1, unary negation was applied
● An operator is binary if it has two operands. The same minus exists in binary form as
well:
let x = 1, y = 3;
alert( y - x ); // 2, binary minus subtracts values
Formally, in the examples above we have two different operators that share the same symbol:
the negation operator, a unary operator that reverses the sign, and the subtraction operator, a
binary operator that subtracts one number from another.
● Remainder %,
● Exponentiation **.
The first four are straightforward, while % and ** need a few words about them.
Remainder % (modulo)
The remainder operator %, despite its appearance, is not related to percents.
The result of a % b is the remainder of the integer division of a by b.
For instance:
alert( 5 % 2 ); // 1, a remainder of 5 divided by 2
alert( 8 % 3 ); // 2, a remainder of 8 divided by 3
Returns the remainder left over after you've divided the left number into a number of
integer portions equal to the right number.
8 % 3 (returns 2, as three goes into 8 twice, leaving 2 left over).
Exponentiation ** (Exponent)
The exponentiation operator a ** b multiplies a by itself b times.
For instance:
alert( 2 ** 2 ); // 4 (2 multiplied by itself 2 times)
alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 times)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 times)
Mathematically, the exponentiation is defined for non-integer numbers as well. For example, a
square root is an exponentiation by 1/2:
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root)
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
Raises a base number to the exponent power, that is, the base number multiplied by itself,
exponent times. It was first Introduced in EcmaScript 2016.
5 ** 2 (returns 25, which is the same as 5 * 5).
let y = -2;
alert( +y ); // -2
// Converts non-numbers
alert( +true ); // 1
alert( +"" ); // 0
It actually does the same thing as Number(...), but is shorter.
The need to convert strings to numbers arises very often. For example, if we are getting values
from HTML form fields, they are usually strings. What if we want to sum them?
The binary plus would add them as strings:
let apples = "2";
let oranges = "3";
Let's look at the last example from above, assuming that num2 holds the value 50 and num1
holds the value 10 (as originally stated above):
num2 + num1 / 8 + 2;
As a human being, you may read this as "50 plus 10 equals 60", then "8 plus 2 equals 10", and
finally "60 divided by 10 equals 6".
But the browser does "10 divided by 8 equals 1.25", then "50 plus 1.25 plus 2 equals 53.25".
This is because of operator precedence — some operators are applied before others when
calculating the result of a calculation (referred to as an expression, in programming).
Operator precedence in JavaScript is the same as is taught in math classes in school —
Multiply and divide are always done first, then add and subtract (the calculation is always
evaluated from left to right).
If you want to override operator precedence, you can put parentheses round the parts that
you want to be explicitly dealt with first. So to get a result of 6, we could do this:
(num2 + num1) / (8 + 2);
Note that you can quite happily use other variables on the right hand side of each
expression, for example:
let x = 3; // x contains the value 3
let y = 4; // y contains the value 4
x *= y; // x now contains the value 12
Note: There are lots of other assignment operators available, but these are the
basic ones you should learn now.
Let’s note that an assignment = is also an operator. It is listed in the precedence table with a
very low priority of 3.
That’s why, when we assign a variable, like x = 2 * 2 + 1, the calculations are done first and
then the = is evaluated, storing the result in x.
let x = 2 * 2 + 1;
alert( x ); // 5
Chaining assignments
Another interesting feature is the ability to chain assignments:
let a, b, c;
a = b = c = 2 + 2;
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
Chained assignments evaluate from right to left. First, the rightmost expression 2 + 2 is
evaluated and then assigned to the variables on the left: c, b and a. At the end, all the variables
share a single value.
Once again, for the purposes of readability it’s better to split such code into few lines:
c = 2 + 2;
b = c;
a = c;
That’s easier to read, especially when eye-scanning the code fast.
Modify-in-place
We often need to apply an operator to a variable and store the new result in that same variable.
For example:
let n = 2;
n = n + 5;
n = n * 2;
This notation can be shortened using the operators += and *=:
let n = 2;
n += 5; // now n = 7 (same as n = n + 5)
n *= 2; // now n = 14 (same as n = n * 2)
alert( n ); // 14
Short “modify-and-assign” operators exist for all arithmetical and bitwise operators: /=, -=, etc.
Such operators have the same precedence as a normal assignment, so they run after most
other calculations:
let n = 2;
n *= 3 + 5;
alert( n ); // 16 (right part evaluated first, same as n *= 8)
Increment/decrement operators
Increasing or decreasing a number by one is among the most common numerical operations.
So, there are special operators for it:
Increment ++ increases a variable by 1:
let counter = 2;
counter++; // works the same as counter = counter + 1, but is shorter
alert( counter ); // 3
Decrement -- decreases a variable by 1:
let counter = 2;
counter--; // works the same as counter = counter - 1, but is shorter
alert( counter ); // 1
Important:
Increment/decrement can only be applied to variables. Trying to use it on a value like 5++
will give an error.
The operators ++ and -- can be placed either before or after a variable.
● When the operator goes after the variable, it is in “postfix form”: counter++.
● The “prefix form” is when the operator goes before the variable: ++counter.
Both of these statements do the same thing: increase counter by 1.
Is there any difference? Yes, but we can only see it if we use the returned value of ++/--.
As we know, all operators return a value. Increment/decrement is no exception. The prefix form
returns the new value while the postfix form returns the old value (prior to
increment/decrement).
To see the difference, here’s an example:
let counter = 1;
let a = ++counter; // (*)
alert(a); // 2
In the line (*), the prefix form ++counter increments counter and returns the new value, 2. So,
the alert shows 2.
Now, let’s use the postfix form:
let counter = 1;
let a = counter++; // (*) changed ++counter to counter++
alert(a); // 1
In the line (*), the postfix form counter++ also increments counter but returns the old value
(prior to increment). So, the alert shows 1.
To summarize:
● If the result of increment/decrement is not used, there is no difference in which form to
use:
let counter = 0;
counter++;
++counter;
alert( counter ); // 2, the lines above did the same
● If we’d like to increase a value and immediately use the result of the operator, we need
the prefix form:
let counter = 0;
alert( ++counter ); // 1
● If we’d like to increment a value but use its previous value, we need the postfix form:
let counter = 0;
alert( counter++ ); // 0
Comma ,
The comma operator , is one of the rarest and most unusual operators. Sometimes, it’s used to
write shorter code, so we need to know it in order to understand what’s going on.
The comma operator allows us to evaluate several expressions, dividing them with a comma ,.
Each of them is evaluated but only the result of the last one is returned.
For example:
let a = (1 + 2, 3 + 4);
alert( a ); // 7 (the result of 3 + 4)
Here, the first expression 1 + 2 is evaluated and its result is thrown away. Then, 3 + 4 is
evaluated and returned as the result.
Comma operator
,
The comma operator allows multiple expressions to be evaluated in a single
statement and returns the result of the last expression.
Tasks
The postfix and prefix forms
importance: 5
What are the final values of all variables a, b, c and d after the code below?
let a = 1, b = 1;
let c = ++a; // ?
let d = b++; // ?
solution
The answer is:
● a = 2
● b = 2
● c = 2
● d = 1
let a = 1, b = 1;
let x = 1 + (a *= 2);
solution
The answer is:
● a = 4 (multiplied by 2)
● x = 5 (calculated as 1 + 4)
Type conversions
importance: 5
What are results of these expressions?
"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
4 + 5 + "px"
"$" + 4 + 5
"4" - 2
"4px" - 2
7 / 0
" -9 " + 5
" -9 " - 5
null + 1
undefined + 1
" \t \n" - 2
Think well, write down and then compare with the answer.
solution
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
null + 1 = 1 // (5)
undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)
1. The addition with a string "" + 1 converts 1 to a string: "" + 1 = "1", and then
we have "1" + 0, the same rule is applied.
2. The subtraction - (like most math operations) only works with numbers, it converts
an empty string "" to 0.
3. The addition with a string appends the number 5 to the string.
4. The subtraction always converts to numbers, so it makes " -9 " a number -9
(ignoring spaces around it).
5. null becomes 0 after the numeric conversion.
6. undefined becomes NaN after the numeric conversion.
7. Space characters, are trimmed off string start and end when a string is converted to
a number. Here the whole string consists of space characters, such as \t, \n and a
“regular” space between them. So, similarly to an empty string, it becomes 0.
Fix the addition
importance: 5
Here’s a code that asks the user for two numbers and shows their sum.
It works incorrectly. The output in the example below is 12 (for default prompt values).
Why? Fix it. The result should be 3.
let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);
alert(a + b); // 12
solution
The reason is that prompt returns user input as a string.
So variables have values "1" and "2" respectively.
let a = "1"; // prompt("First number?", 1);
let b = "2"; // prompt("Second number?", 2);
alert(a + b); // 12
What we should do is to convert strings to numbers before +. For example, using Number()
or prepending them with +.
For example, right before prompt:
let a = +prompt("First number?", 1);
let b = +prompt("Second number?", 2);
alert(a + b); // 3
Or in the alert:
let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);
alert(+a + +b); // 3
Using both unary and binary + in the latest code. Looks funny, doesn’t it?
Which operation you choose depends on what sort of comparison you are looking to
perform. Briefly:
● double equals (==) will perform a type conversion when comparing two things, and
will handle NaN, -0, and +0 specially to conform to IEEE 754 (so NaN != NaN, and -0
== +0);
● triple equals (===) will do the same comparison as double equals (including the
special handling for NaN, -0, and +0) but without type conversion; if the types differ,
false is returned.
● [Link] does no type conversion and no special handling for NaN, -0, and +0
(giving it the same behavior as === except on those special numeric values).
Note that the distinction between these all have to do with their handling of primitives; none
of them compares whether the parameters are conceptually similar in structure. For any
non-primitive objects x and y which have the same structure but are distinct objects
themselves, all of the above forms will evaluate to false.
Strict equality is almost always the correct comparison operation to use. For all values
except numbers, it uses the obvious semantics: a value is only equal to itself. For
numbers it uses slightly different semantics to gloss over two different edge cases.
The first is that floating point zero is either positively or negatively signed. This is useful in
representing certain mathematical solutions, but as most situations don't care about the
difference between +0 and -0, strict equality treats them as the same value.
The second is that floating point includes the concept of a not-a-number value, NaN, to
represent the solution to certain ill-defined mathematical problems: negative infinity
added to positive infinity, for example. Strict equality treats NaN as unequal to every other
value -- including itself. (The only case in which (x !== x) is true is when x is NaN.)
A strict equality operator === checks the equality without type conversion.
In other words, if a and b are of different types, then a === b immediately returns false
without an attempt to convert them.
Let’s try it:
alert( 0 === false ); // false, because the types are different
The strict equality operator is a bit longer to write, but makes it obvious what’s going on and
leaves less room for errors.
Tests whether the left and right values are identical to one another
In most cases, using loose equality is discouraged. The result of a comparison using strict
equality is easier to predict, and may evaluate more quickly due to the lack of type
coercion.
Weird consequences
It is possible that at the same time:
● Two values are equal.
● One of them is true as a boolean and the other one is false as a boolean.
For example:
let a = 0;
alert( Boolean(a) ); // false
let b = "0";
alert( Boolean(b) ); // true
alert(a == b); // true!
From JavaScript’s standpoint, this result is quite normal. An equality check converts values
using the numeric conversion (hence "0" becomes 0), while the explicit Boolean
conversion uses another set of rules.
Strict-non-equality (!==)
There is also a “strict non-equality” operator !== analogous to !=.
Tests whether the left and right values are not identical to one another
Note: You may see some people using == and != in their tests for equality and non-equality.
These are valid operators in JavaScript, but they differ from ===/!==. The former versions test
whether the values are the same but not whether the values' datatypes are the same. The
latter, strict versions test the equality of both the values and their datatypes. The strict
versions tend to result in fewer errors, so we recommend you use them.
If you try entering some of these values in a console, you'll see that they all return true/false
values — those booleans we mentioned in the last article. These are very useful, as they allow
us to make decisions in our code, and they are used every time we want to make a choice of
some kind. For example, booleans can be used to:
● Display the correct text label on a button depending on whether a feature is turned on or
off
● Display a game over message if a game is over or a victory message if the game has
been won
● Display the correct seasonal greeting depending what holiday season it is
● Zoom a map in or out depending on what zoom level is selected
We'll look at how to code such logic when we look at conditional statements in a future article.
For now, let's look at a quick example:
<button>Start machine</button>
<p>The machine is stopped.</p>
[Link]('click', updateBtn);
function updateBtn() {
if ([Link] === 'Start machine') {
[Link] = 'Stop machine';
[Link] = 'The machine has started!';
} else {
[Link] = 'Start machine';
[Link] = 'The machine is stopped.';
}
}
Open in new window
You can see the equality operator being used just inside the updateBtn() function. In this case,
we are not testing if two mathematical expressions have the same value — we are testing
whether the text content of a button contains a certain string — but it is still the same principle at
work. If the button is currently saying "Start machine" when it is pressed, we change its label to
"Stop machine", and update the label as appropriate. If the button is currently saying "Stop
machine" when it is pressed, we swap the display back again.
Note: Such a control that swaps between two states is generally referred to as a toggle. It
toggles between one state and another — light on, light off, etc.
function attemptMutation(v) {
[Link](Number, 'NEGATIVE_ZERO', { value: v });
}
Same-value-zero equality
Similar to same-value equality, but +0 and -0 are considered equal.
undefin undefin t t t t
ed ed
null null t t t t
true true t t t t
false false t t t t
'foo' 'foo' t t t t
0 0 t t t t
+0 -0 t t f t
+0 0 t t t t
-0 0 t t f t
0n -0n t t t t
0 false t f f f
"" false t f f f
"" 0 t f f f
'0' 0 t f f f
'17' 17 t f f f
[1, 2] '1,2' t f f f
new 'foo' t f f f
String(
'foo')
null undefin t f f f
ed
null false f f f f
undefin false f f f f
ed
{ foo: { foo: f f f f
'bar' } 'bar' }
new new f f f f
String( String(
'foo') 'foo')
0 null f f f f
0 NaN f f f f
'foo' NaN f f f f
NaN NaN f f t t
Avoid problems
Why did we go over these examples? Should we remember these peculiarities all the time? Well,
not really. Actually, these tricky things will gradually become familiar over time, but there’s a
solid way to avoid problems with them:
● Treat any comparison with undefined/null except the strict equality === with
exceptional care.
● Don’t use comparisons >= > < <= with a variable that may be null/undefined, unless
you’re really sure of what you’re doing. If a variable can have these values, check for
them separately.
Summary
● Comparison operators return a boolean value.
● Strings are compared letter-by-letter in the “dictionary” order.
● When values of different types are compared, they get converted to numbers (with the
exclusion of a strict equality check).
● The values null and undefined equal == each other and do not equal any other value.
● Be careful when using comparisons like > or < with variables that can occasionally be
null/undefined. Checking for null/undefined separately is a good idea.
Tasks
Comparisons
importance: 5
What will be the result for these expressions?
5 > 4
"apple" > "pineapple"
"2" > "12"
undefined == null
undefined === null
null == "\n0\n"
null === +"\n0\n"
solution
5 > 4 → true
"apple" > "pineapple" → false
"2" > "12" → true
undefined == null → true
undefined === null → false
null == "\n0\n" → false
null === +"\n0\n" → false
Some of the reasons:
1. Obviously, true.
2. Dictionary comparison, hence false. "a" is smaller than "p".
3. Again, dictionary comparison, first char "2" is greater than the first char "1".
4. Values null and undefined equal each other only.
5. Strict equality is strict. Different types from both sides lead to false.
6. Similar to (4), null only equals undefined.
7. Strict equality of different types.
Logical operators
There are three logical operators in JavaScript: || (OR), && (AND), ! (NOT).
Although they are called “logical”, they can be applied to values of any type, not only boolean.
Their result can also be of any type.
Let’s see the details.
|| (OR)
The “OR” operator is represented with two vertical line symbols:
result = a || b;
In classical programming, the logical OR is meant to manipulate boolean values only. If any of
its arguments are true, it returns true, otherwise it returns false.
In JavaScript, the operator is a little bit trickier and more powerful. But first, let’s see what
happens with boolean values.
There are four possible logical combinations:
alert( true || true ); // true
alert( false || true ); // true
alert( true || false ); // true
alert( false || false ); // false
As we can see, the result is always true except for the case when both operands are false.
If an operand is not a boolean, it’s converted to a boolean for the evaluation.
For instance, the number 1 is treated as true, the number 0 as false:
if (1 || 0) { // works just like if( true || false )
alert( 'truthy!' );
}
Most of the time, OR || is used in an if statement to test if any of the given conditions is true.
For example:
let hour = 9;
if (hour < 10 || hour > 18) {
alert( 'The office is closed.' );
}
We can pass more conditions:
let hour = 12;
let isWeekend = true;
if (hour < 10 || hour > 18 || isWeekend) {
alert( 'The office is closed.' ); // it is the weekend
}
In other words, a chain of OR || returns the first truthy value or the last one if no truthy value is
found.
For instance:
alert( 1 || 0 ); // 1 (1 is truthy)
alert( null || 1 ); // 1 (1 is the first truthy value)
alert( null || 0 || 1 ); // 1 (the first truthy value)
alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
This leads to some interesting usage compared to a “pure, classical, boolean-only OR”.
1. Getting the first truthy value from a list of variables or expressions.
For instance, we have firstName, lastName and nickName variables, all optional (i.e.
can be undefined or have falsy values).
Let’s use OR || to choose the one that has the data and show it (or "Anonymous" if
nothing set):
let firstName = "";
let lastName = "";
let nickName = "SuperCoder";
alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder
If all variables were falsy, "Anonymous" would show up.
Syntax
a | b
Description
The operands are converted to 32-bit integers and expressed by a series of bits (zeroes
and ones). Numbers with more than 32 bits get their most significant bits discarded. For
example, the following integer with more than 32 bits will be converted to a 32 bit integer:
Before: 11100110111110100000000000000110000000000001
After: 10100000000000000110000000000001
Each bit in the first operand is paired with the corresponding bit in the second operand:
first bit to first bit, second bit to second bit, and so on.
The operator is applied to each pair of bits, and the result is constructed bitwise.
The truth table for the OR operation is:
a b a OR b
0 0 0
0 1 1
1 0 1
1 1 1
. 9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)
Examples
Using bitwise OR
// 9 (00000000000000000000000000001001)
// 14 (00000000000000000000000000001110)
14 | 9;
// 15 (00000000000000000000000000001111)
&& (AND)
The AND operator is represented with two ampersands &&:
result = a && b;
In classical programming, AND returns true if both operands are truthy and false otherwise:
alert( true && true ); // true
alert( false && true ); // false
alert( true && false ); // false
alert( false && false ); // false
An example with if:
let hour = 12;
let minute = 30;
! (NOT)
The boolean NOT operator is represented with an exclamation sign !.
The syntax is pretty simple:
result = !value;
The operator accepts a single argument and does the following:
1. Converts the operand to boolean type: true/false.
2. Returns the inverse value.
For instance:
alert( !true ); // false
alert( !0 ); // true
A double NOT !! is sometimes used for converting a value to boolean type:
alert( !!"non-empty string" ); // true
alert( !!null ); // false
That is, the first NOT converts the value to boolean and returns the inverse, and the second NOT
inverses it again. In the end, we have a plain value-to-boolean conversion.
There’s a little more verbose way to do the same thing – a built-in Boolean function:
alert( Boolean("non-empty string") ); // true
alert( Boolean(null) ); // false
The precedence of NOT ! is the highest of all logical operators, so it always executes first,
before && or ||.
Truthy / Falsy
A truthy or falsy value is a value that is being casted into a boolean when evaluated in a boolean
context. An example of boolean context would be the evaluation of an if condition:
Every value will be casted to true unless they are equal to:
false
0
“”(empty string)
null
undefined
NaN
examples of boolean context:
if condition evaluation
if (myVar) {}
myVar can be any first-class citizen (variable, function, boolean) but it will be casted into a boolean
because it's evaluated in a boolean context.
Tasks
What's the result of OR?
importance: 5
What is the code below going to output?
alert( null || 2 || undefined );
solution
The answer is 2, that’s the first truthy value.
alert( null || 2 || undefined );
What's the result of OR'ed alerts?
importance: 3
What will the code below output?
alert( alert(1) || 2 || alert(3) );
solution
The answer: first 1, then 2.
alert( alert(1) || 2 || alert(3) );
The call to alert does not return a value. Or, in other words, it returns undefined.
1. The first OR || evaluates its left operand alert(1). That shows the first message
with 1.
2. The alert returns undefined, so OR goes on to the second operand searching for
a truthy value.
3. The second operand 2 is truthy, so the execution is halted, 2 is returned and then
shown by the outer alert.
There will be no 3, because the evaluation does not reach alert(3).
What is the result of AND?
importance: 5
What is this code going to show?
alert( 1 && null && 2 );
solution
The answer: null, because it’s the first falsy value from the list.
alert( 1 && null && 2 );
What is the result of AND'ed alerts?
importance: 3
What will this code show?
alert( alert(1) && alert(2) );
solution
The answer: 1, and then undefined.
alert( alert(1) && alert(2) );
The call to alert returns undefined (it just shows a message, so there’s no meaningful
return).
Because of that, && evaluates the left operand (outputs 1), and immediately stops, because
undefined is a falsy value. And && looks for a falsy value and returns it, so it’s done.
The result of OR AND OR
importance: 5
What will the result be?
alert( null || 2 && 3 || 4 );
solution
The answer: 3.
alert( null || 2 && 3 || 4 );
The precedence of AND && is higher than ||, so it executes first.
The result of 2 && 3 = 3, so the expression becomes:
null || 3 || 4
Now the result is the first truthy value: 3.
Check the range between
importance: 3
Write an if condition to check that age is between 14 and 90 inclusively.
“Inclusively” means that age can reach the edges 14 or 90.
solution
if (age >= 14 && age <= 90)
Check the range outside
importance: 3
Write an if condition to check that age is NOT between 14 and 90 inclusively.
Create two variants: the first one using NOT !, the second one – without it.
solution
The first variant:
if (!(age >= 14 && age <= 90))
The second variant:
if (age < 14 || age > 90)
A question about "if"
importance: 5
Which of these alerts are going to execute?
What will the results of the expressions be inside if(...)?
if (-1 || 0) alert( 'first' );
if (-1 && 0) alert( 'second' );
if (null || -1 && 1) alert( 'third' );
solution
The answer: the first and the third will execute.
Details:
// Runs.
// The result of -1 || 0 = -1, truthy
if (-1 || 0) alert( 'first' );
// Doesn't run
// -1 && 0 = 0, falsy
if (-1 && 0) alert( 'second' );
// Executes
// Operator && has a higher precedence than ||
// so -1 && 1 executes first, giving us the chain:
// null || -1 && 1 -> null || 1 -> 1
if (null || -1 && 1) alert( 'third' );
Check the login
importance: 3
Write the code which asks for a login with prompt.
If the visitor enters "Admin", then prompt for a password, if the input is an empty line or Esc –
show “Canceled”, if it’s another string – then show “I don’t know you”.
The password is checked as follows:
● If it equals “TheMaster”, then show “Welcome!”,
● Another string – show “Wrong password”,
● For an empty string or cancelled input, show “Canceled”
The schema:
Please use nested if blocks. Mind the overall readability of the code.
Hint: passing an empty input to a prompt returns an empty string ''. Pressing ESC during a
prompt returns null.
Run the demo
solution
let userName = prompt("Who's there?", '');
Comparison with ||
The OR || operator can be used in the same way as ??, as it was described in the previous
chapter.
For example, in the code above we could replace ?? with || and still get the same result:
let firstName = null;
let lastName = null;
let nickName = "Supercoder";
// shows the first truthy value:
alert(firstName || lastName || nickName || "Anonymous"); // Supercoder
The OR || operator exists since the beginning of JavaScript, so developers were using it for
such purposes for a long time.
On the other hand, the nullish coalescing operator ?? was added to JavaScript only recently,
and the reason for that was that people weren’t quite happy with ||.
The important difference between them is that:
● || returns the first truthy value.
● ?? returns the first defined value.
In other words, || doesn’t distinguish between false, 0, an empty string "" and
null/undefined. They are all the same – falsy values. If any of these is the first argument of
||, then we’ll get the second argument as the result.
In practice though, we may want to use the default value only when the variable is
null/undefined. That is when the value is really unknown/not set.
For example, consider this:
let height = 0;
alert(height || 100); // 100
alert(height ?? 100); // 0
● The height || 100 checks height for being a falsy value, and it really is.
● so the result is the second argument, 100.
● The height ?? 100 checks height for being null/undefined, and it’s not,
● so the result is height “as is”, that is 0.
If the zero height is a valid value, that shouldn’t be replaced with the default, then ?? does just
the right thing.
Precedence
The precedence of the ?? operator is rather low: 5 in the MDN table. So ?? is evaluated before =
and ?, but after most other operations, such as +, *.
So if we’d like to choose a value with ?? in an expression with other operators, consider adding
parentheses:
let height = null;
let width = null;
// important: use parentheses
let area = (height ?? 100) * (width ?? 50);
alert(area); // 5000
Otherwise, if we omit parentheses, then as * has the higher precedence than ??, it would
execute first, leading to incorrect results.
// without parentheses
let area = height ?? 100 * width ?? 50;
// ...works the same as this (probably not what we want):
let area = height ?? (100 * width) ?? 50;
Using ?? with && or ||
Due to safety reasons, JavaScript forbids using ?? together with && and || operators, unless
the precedence is explicitly specified with parentheses.
The code below triggers a syntax error:
let x = 1 && 2 ?? 3; // Syntax error
The limitation is surely debatable, but it was added to the language specification with the
purpose to avoid programming mistakes when people start to switch to ?? from ||.
Use explicit parentheses to work around it:
let x = (1 && 2) ?? 3; // Works
alert(x); // 2
Syntax
expr1 ??= expr2
Short-circuit evaluation
The nullish coalescing operator is evaluated left to right, it is tested for possible short-circuit
evaluation using the following rule:
(some expression that is neither null nor undefined) ?? expr is short-circuit
evaluated to the left-hand side expression if the left-hand side proves to be neither null nor
undefined.
Short circuit means that the expr part above is not evaluated, hence any side effects of doing
so do not take effect (e.g., if expr is a function call, the calling never takes place).
Logical nullish assignment short-circuits as well meaning that x ??= y is equivalent to:
x ?? (x = y);
And not equivalent to the following which would always perform an assignment:
x = x ?? y;
Examples
Using logical nullish assignment
function config(options) {
[Link] ??= 100;
[Link] ??= 25;
return options;
}
config({ duration: 125 }); // { duration: 125, speed: 25 }
config({}); // { duration: 100, speed: 25 }
Summary
● The nullish coalescing operator ?? provides a short way to choose the first “defined”
value from a list.
It’s used to assign default values to variables:
// set height=100, if height is null or undefined
● height = height ?? 100;
● The operator ?? has very low precedence, only a bit higher than ? and =, so consider
adding parentheses when using it in an expression.
● It’s forbidden to use it with || or && without explicit parentheses.
21 Grouping n/a ( … )
20 Member left-to … . …
Access -right
Computed left-to … [ … ]
Member -right
Access
Optional left-to ?.
chaining -right
18 Postfix n/a … ++
Increment
Postfix … --
Decrement
17 Logical right-t ! …
NOT (!) o-left
Bitwise ~ …
NOT (~)
Unary plus + …
(+)
Unary - …
negation
(-)
Prefix ++ …
Increment
Prefix -- …
Decrement
typeof typeof …
void void …
delete delete …
await await …
16 Exponenti right-t … ** …
ation (**) o-left
15 Multiplicati left-to … * …
on (*) -right
Division (/) … / …
Remainder … % …
(%)
14 Addition left-to … + …
(+) -right
Subtractio … - …
n (-)
Bitwise … >> …
Right Shift
(>>)
Bitwise … >>> …
Unsigned
Right Shift
(>>>)
Greater … > …
Than (>)
Greater … >= …
Than Or
Equal (>=)
in … in …
instance …
of instanceof
…
11 Equality left-to … == …
(==) -right
Inequality … != …
(!=)
Strict … === …
Equality
(===)
Strict … !== …
Inequality
(!==)
9 Bitwise left-to … ^ …
XOR (^) -right
8 Bitwise left-to … | …
OR (|) -right
6 Logical left-to … || …
OR (||) -right
5 Nullish left-to … ?? …
coalescing -right
operator
(??)
4 Conditiona right-t … ? … :
l (ternary) o-left …
operator
3 Assignme right-t … = …
nt o-left
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
… &&= …
… ||= …
… ??= …
yield* yield* …
1 Comma / left-to … , …
Sequence -right