break
and continue
break
break
plus label: leaving any labeled statementcontinue
if
statements
if
statementsswitch
statements
while
loops
while
loopsdo-while
loopsfor
loops
for
loopsfor-of
loops
const
: for-of
vs. for
for-await-of
loopsfor-in
loops (avoid)This chapter covers the following control flow statements:
if
statement (ES1)switch
statement (ES3)while
loop (ES1)do-while
loop (ES3)for
loop (ES1)for-of
loop (ES6)for-await-of
loop (ES2018)for-in
loop (ES1)Before we get to the actual control flow statements, let’s take a look at two operators for 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.
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:
for (const x of ['a', 'b', 'c']) {
console.log(x);
if (x === 'b') break;
console.log('---')
}
// Output:
// 'a'
// '---'
// 'b'
break
plus label: leaving any labeled statementbreak
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
:
In the following example, we use break with a label to leave a loop differently when we succeeded (line A). Then we skip what comes directly after the loop, which is where we end up if we failed.
function findSuffix(stringArray, suffix) {
let result;
search_block: {
for (const str of stringArray) {
if (str.endsWith(suffix)) {
// Success:
result = str;
break search_block; // (A)
}
} // for
// Failure:
result = '(Untitled)';
} // search_block
return { suffix, result };
// Same as: {suffix: suffix, result: result}
}
assert.deepEqual(
findSuffix(['foo.txt', 'bar.html'], '.html'),
{ suffix: '.html', result: 'bar.html' }
);
assert.deepEqual(
findSuffix(['foo.txt', 'bar.html'], '.mjs'),
{ suffix: '.mjs', result: '(Untitled)' }
);
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:
const lines = [
'Normal line',
'# Comment',
'Another normal line',
];
for (const line of lines) {
if (line.startsWith('#')) continue;
console.log(line);
}
// Output:
// 'Normal line'
// 'Another normal line'
if
statementsThese 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:
if (cond1) {
// ···
} else if (cond2) {
// ···
}
if (cond1) {
// ···
} else if (cond2) {
// ···
} else {
// ···
}
You can continue this chain with more else if
s.
if
statementsThe general syntax of if
statements is:
So far, the then_statement
has always been a block, but we can use any 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.
switch
statementsA switch
statement looks as follows:
switch («switch_expression») {
«switch_body»
}
The body of switch
consists of zero or more case clauses:
case «case_expression»:
«statements»
And, optionally, a default clause:
default:
«statements»
A switch
is executed as follows:
switch
statementLet’s look at an example: The following function converts a number from 1–7 to the name of a weekday.
function dayOfTheWeek(num) {
switch (num) {
case 1:
return 'Monday';
case 2:
return 'Tuesday';
case 3:
return 'Wednesday';
case 4:
return 'Thursday';
case 5:
return 'Friday';
case 6:
return 'Saturday';
case 7:
return 'Sunday';
}
}
assert.equal(dayOfTheWeek(5), 'Friday');
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 englishToFrench(english) {
let french;
switch (english) {
case 'hello':
french = 'bonjour';
case 'goodbye':
french = 'au revoir';
}
return french;
}
// The result should be 'bonjour'!
assert.equal(englishToFrench('hello'), 'au revoir');
That is, our implementation of dayOfTheWeek()
only worked, because we used return
. We can fix englishToFrench()
by using break
:
function englishToFrench(english) {
let french;
switch (english) {
case 'hello':
french = 'bonjour';
break;
case 'goodbye':
french = 'au revoir';
break;
}
return french;
}
assert.equal(englishToFrench('hello'), 'bonjour'); // ok
The statements of a case clause can be omitted, which effectively gives us multiple case expressions per case clause:
function isWeekDay(name) {
switch (name) {
case 'Monday':
case 'Tuesday':
case 'Wednesday':
case 'Thursday':
case 'Friday':
return true;
case 'Saturday':
case 'Sunday':
return false;
}
}
assert.equal(isWeekDay('Wednesday'), true);
assert.equal(isWeekDay('Sunday'), false);
default
clauseA 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'});
Exercises:
switch
exercises/control-flow/number_to_month_test.mjs
exercises/control-flow/is_object_via_switch_test.mjs
while
loopsA while
loop has the following syntax:
while («condition») {
«statements»
}
Before each loop iteration, while
evaluates condition
:
while
body is executed one more time.while
loopsThe following code uses a while
loop. In each loop iteration, it removes the first element of arr
via .shift()
and logs it.
const arr = ['a', 'b', 'c'];
while (arr.length > 0) {
const elem = arr.shift(); // remove first element
console.log(elem);
}
// Output:
// 'a'
// 'b'
// 'c'
If the condition always evaluates to true
, then while
is an infinite loop:
do-while
loopsThe do-while
loop works much like while
, but it checks its condition after each loop iteration (not before).
prompt()
is a global function that is available in web browsers. It prompts the user to input text and returns it.
for
loopsA for
loop has the following syntax:
for («initialization»; «condition»; «post_iteration») {
«statements»
}
The first line is the head of the loop and controls how often the body (the remainder of the loop) is executed. It has three parts and each of them is optional:
initialization
: sets up variables etc. for the loop. Variables declared here via let
or const
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.A for
loop is therefore roughly equivalent to the following while
loop:
«initialization»
while («condition») {
«statements»
«post_iteration»
}
for
loopsAs 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:
const arr = ['a', 'b', 'c'];
for (let i=0; i<3; i++) {
console.log(arr[i]);
}
// Output:
// 'a'
// 'b'
// 'c'
If you omit all three parts of the head, you get an infinite loop:
for-of
loopsA 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:
const iterable = ['hello', 'world'];
for (const elem of iterable) {
console.log(elem);
}
// Output:
// 'hello'
// 'world'
But you can also use a (mutable) variable that already exists:
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.
As mentioned before, for-of
works with any iterable object, not just with Arrays. For example, with Sets:
Lastly, you can also use for-of
to iterate over the [index, element] entries of Arrays:
const arr = ['a', 'b', 'c'];
for (const [index, elem] of arr.entries()) {
console.log(`${index} -> ${elem}`);
}
// Output:
// '0 -> a'
// '1 -> b'
// '2 -> c'
With [index, element]
, we are using destructuring to access Array elements.
Exercise:
for-of
exercises/control-flow/array_to_string_test.mjs
for-await-of
loopsfor-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 the chapter on asynchronous iteration.
for-in
loops (avoid) Recommendation: don’t use
for-in
loops
for-in
has several pitfalls. Therefore, it is usually best to avoid it.
This is an example of using for-in
properly, which involves boilerplate code (line A):
function getOwnPropertyNames(obj) {
const result = [];
for (const key in obj) {
if ({}.hasOwnProperty.call(obj, key)) { // (A)
result.push(key);
}
}
return result;
}
assert.deepEqual(
getOwnPropertyNames({ a: 1, b:2 }),
['a', 'b']);
assert.deepEqual(
getOwnPropertyNames(['a', 'b']),
['0', '1']); // strings!
We can implement the same functionality without for-in
, which is almost always better:
function getOwnPropertyNames(obj) {
const result = [];
for (const key of Object.keys(obj)) {
result.push(key);
}
return result;
}
Quiz
See quiz app.