21. Control flow statements
This chapter covers the following control flow statements:
if
statements (ES1)switch
statements (ES3)while
loops (ES1)do-while
loops (ES3)for
loops (ES1)for-of
loops (ES6)for-await-of
loops (ES2018)for-in
loops (ES1)
Before we get to the actual control flow statements, let’s take a look at two operators for controlling loops.
21.1. Controlling loops: break and continue
The two operators break
and continue
can be used to control loops and other statements while you are inside them.
21.1.1. break
There are two versions of break
: one with an operand and one without an operand. The latter version works inside the following statements: while
, do-while
, for
, for-of
, for-await-of
, for-in
and switch
. It immediately leaves the current statement:
21.1.2. Additional use case for break: leaving blocks
break
with an operand works everywhere. Its operand is a label. Labels can be put in front of any statement, including blocks. break foo
leaves the statement whose label is foo
:
Breaking from blocks is occasionally handy if you are using a loop and want to distinguish between finding what you were looking for and finishing the loop without success:
function search(stringArray, suffix) {
let result;
search_block: {
for (const str of stringArray) {
if (str.endsWith(suffix)) {
// Success
result = str;
break search_block;
}
} // for
// Failure
result = '(Untitled)';
} // search_block
return { suffix, result };
// same as: {suffix: suffix, result: result}
}
assert.deepEqual(
search(['foo.txt', 'bar.html'], '.html'),
{ suffix: '.html', result: 'bar.html' }
);
assert.deepEqual(
search(['foo.txt', 'bar.html'], '.js'),
{ suffix: '.js', result: '(Untitled)' }
);
21.1.3. continue
continue
only works inside while
, do-while
, for
, for-of
, for-await-of
and for-in
. It immediately leaves the current loop iteration and continues with the next one. For example:
21.2. if statements
These are two simple if
statements: One with just a “then” branch and one with both a “then” branch and an “else” branch:
Instead of the block, else
can also be followed by another if
statement:
You can continue this chain with more else if
s.
21.2.1. The syntax of if statements
The general syntax of if
statements is:
So far, the then_statement
has always been a block, but you can also use a statement. That statement must be terminated with a semicolon:
That means that else if
is not its own construct, it’s simply an if
statement whose else_statement
is another if
statement.
21.3. switch statements
The head of a switch
statement looks as follows:
switch («switch_expression») {
«switch_body»
}
Inside the body of switch
, there are zero or more case clauses:
case «case_expression»:
«statements»
And, optionally, a default clause:
default:
«statements»
A switch
is executed as follows:
- Evaluate the switch expression.
- Jump to the first case clause whose expression has the same result as the switch expression.
- If there is no such case clause, jump to the default clause.
- If there is no default clause, nothing happens.
21.3.1. A first example
Let’s look at an example: The following function converts a number from 1–7 to the name of a weekday.
21.3.2. Don’t forget to return or break!
At the end of a case clause, execution continues with the next case clause (unless you return
or break
). For example:
function dayOfTheWeek(num) {
let name;
switch (num) {
case 1:
name = 'Monday';
case 2:
name = 'Tuesday';
case 3:
name = 'Wednesday';
case 4:
name = 'Thursday';
case 5:
name = 'Friday';
case 6:
name = 'Saturday';
case 7:
name = 'Sunday';
}
return name;
}
assert.equal(dayOfTheWeek(5), 'Sunday'); // not 'Friday'!
That is, the previous implementation of dayOfTheWeek()
only worked, because we used return
. We can fix this implementation by using break
:
function dayOfTheWeek(num) {
let name;
switch (num) {
case 1:
name = 'Monday';
break;
case 2:
name = 'Tuesday';
break;
case 3:
name = 'Wednesday';
break;
case 4:
name = 'Thursday';
break;
case 5:
name = 'Friday';
break;
case 6:
name = 'Saturday';
break;
case 7:
name = 'Sunday';
break;
}
return name;
}
assert.equal(dayOfTheWeek(5), 'Friday');
21.3.3. Empty cases clauses
The statements of a case clause can be omitted, which effectively gives us multiple case expressions per case clause:
21.3.4. Checking for illegal values via a default clause
A default
clause is jumped to if the switch
expression has no other match. That makes it useful for error checking:
function isWeekDay(name) {
switch (name) {
case 'Monday':
case 'Tuesday':
case 'Wednesday':
case 'Thursday':
case 'Friday':
return true;
case 'Saturday':
case 'Sunday':
return false;
default:
throw new Error('Illegal value: '+name);
}
}
assert.throws(
() => isWeekDay('January'),
{message: 'Illegal value: January'});
21.4. while loops
A while
loop has the following syntax:
while («condition») {
«statements»
}
Before each loop iteration, while
evaluates condition
:
- If the result is falsy, the loop is finished.
- If the result is truthy, the
while
body is executed one more time.
21.4.1. Examples
The following code uses a while
loop. In each loop iteration, it removes the first element of arr
via .shift()
and logs it.
If the condition is true
then while
is an infinite loop:
21.5. do-while loops
The do-while
loop works much like while
, but it checks its condition after each loop iteration (not before).
21.6. for loops
With a for
loop, you use the head to control how its body is executed. The head has three parts and each of them is optional:
for («initialization»; «condition»; «post_iteration») {
«statements»
}
initialization
: sets up variables etc. for the loop. Variables declared here vialet
orconst
only exist inside the loop.condition
: This condition is checked before each loop iteration. If it is falsy, the loop stops.post_iteration
: This code is executed after each loop iteration.
Afor
loop is therefore roughly equivalent to the followingwhile
loop:
«initialization»
while («condition») {
«statements»
«post_iteration»
}
21.6.1. Examples
As an example, this is how to count from zero to two via a for
loop:
This is how to log the contents of an Array via a for
loop:
If you omit all three parts of the head, you get an infinite loop:
21.7. for-of loops
A for-of
loop iterates over an iterable – a data container that supports the iteration protocol. Each iterated value is stored in a variable, as specified in the head:
for («iteration_variable» of «iterable») {
«statements»
}
The iteration variable is usually created via a variable declaration:
But you can also use a (mutable) variable that already exists:
21.7.1. const: for-of vs. for
Note that, in for-of
loops, you can use const
. The iteration variable can still be different for each iteration (it just can’t change during the iteration). Think of it as a new const
declaration being executed each time, in a fresh scope.
In contrast, in for
loops, you must declare variables via let
or var
if their values change.
21.7.2. Iterating over iterables
As mentioned before, for-of
works with any iterable object, not just with Arrays. For example, with Sets:
21.7.3. Iterating over [index, element] pairs of Arrays
Lastly, you can also use for-of
to iterate over the [index, element] entries of Arrays:
21.8. for-await-of loops
for-await-of
is like for-of
, but it works with asynchronous iterables instead of synchronous ones. And it can only be used inside async functions and async generators.
for-await-of
is described in detail in a later chapter.
21.9. for-in loops (avoid)
for-in
has several pitfalls. Therefore, it is usually best to avoid it.
This is an example of using for-in
:
This is a better alternative:
For more information on for-in
, consult “Speaking JavaScript”.