Statements
|
This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve. This section’s bibliography lists the reference material consulted while preparing these pages. |
Statements are the executable units of a JavaScript program — where expressions (see
Functions, Expressions & Operators) produce values,
statements do things: they branch, they loop, and they jump around a function’s control flow. This page covers
the two families that make up most everyday JavaScript control flow: conditionals (branching) and loops
(repetition), plus the jump statements (break/continue) that fine-tune them.
Conditionals
if / else if / else
The if statement is JavaScript’s basic conditional. It evaluates a parenthesized expression and executes the
following statement only if that expression is truthy:
if (username == null) {
username = "Anonymous";
}
An else clause runs when the condition is falsy:
if (n === 1) {
console.log("You have 1 new message.");
} else {
console.log(`You have ${n} new messages.`);
}
else if is not a distinct keyword — it’s the ordinary else clause of one if statement containing another
if statement, chained to express a multiway branch:
if (n === 1) {
// block #1
} else if (n === 2) {
// block #2
} else if (n === 3) {
// block #3
} else {
// fallback
}
Always wrap if/else bodies in { }, even for a single statement. Without braces, an else binds to the
nearest unmatched if, which can silently attach it to the wrong branch when the code is later edited or
reformatted — a classic source of dangling-else bugs.
|
switch
switch avoids re-evaluating the same expression across a long if/else if chain. It evaluates one expression
once, then jumps to the case label whose expression matches it using strict (===) equality:
function typeLabel(x) {
switch (typeof x) {
case "number":
return x.toString(16);
case "string":
return `"${x}"`;
default:
return String(x);
}
}
Execution enters at the matching case and then falls through every subsequent case until it hits a break (or
the end of the block) — case labels mark starting points only, not self-contained branches. Falling through
intentionally is rare and should be commented explicitly when used; the normal pattern is to end every case
(including default) with break, or with return when the switch is the last thing a function does. default
runs when no case matches, and may appear anywhere in the block, though placing it last is the common convention.
Loops
JavaScript has five looping constructs: while, do…while, for, for…of (with its for await…of
variant), and for…in.
while
The simplest loop: test, then run, repeating while the test stays truthy.
let count = 0;
while (count < 10) {
console.log(count);
count++;
}
do…while
Like while, but the test runs after the body, so the body always executes at least once:
do {
console.log(a[i]);
} while (++i < len);
Because it’s uncommon to be sure a loop body must run before its condition is even checked, do…while is the
least-used of the five loop forms in practice.
for
for packages a loop’s initialization, test, and increment into one line, which keeps all three of a counter’s
touch points visible together and makes it harder to forget one of them:
for (let count = 0; count < 10; count++) {
console.log(count);
}
Any of the three clauses may be omitted (the two ; separators are still required) — for (;;) { … } is an
infinite loop, equivalent to while (true).
for…of
for…of iterates the values of any iterable — arrays, strings, Set, Map, and any custom iterable (see
Iterators & Generators for what makes an object iterable):
const data = [1, 2, 3, 4, 5];
let sum = 0;
for (const element of data) {
sum += element;
}
sum; // => 15
Plain objects are not iterable by default — for (const x of someObject) throws a TypeError. To iterate an
object’s own properties with for…of, pair it with Object.keys()/Object.values()/Object.entries() (see
Objects, Properties & Destructuring):
const o = { x: 1, y: 2, z: 3 };
for (const [key, value] of Object.entries(o)) {
console.log(key, value);
}
Strings iterate by Unicode code point (not UTF-16 code unit), so multi-byte characters like emoji are visited once each rather than split in two.
for await…of (ES2018) is the asynchronous counterpart, used with async iterables — see
Asynchronous JavaScript for how it relates to promises and async
generators.
for…in
for…in iterates enumerable property names (as strings) of any object, walking up the prototype chain to
include inherited enumerable properties:
const o = { x: 1, y: 2, z: 3 };
for (const key in o) {
console.log(o[key]);
}
for…in predates for…of and ES6 entirely, and it is easy to reach for out of habit when for…of is what
you actually want — especially with arrays, where for…in yields index strings ("0", "1", …) rather
than element values, and also walks any enumerable properties added to Array.prototype. As a rule of thumb:
default to for…of (with Object.keys()/entries() when you need an object’s own keys), and reach for
for…in only when you deliberately want inherited enumerable properties included.
Loop control: break and continue
break
Unlabeled break exits the innermost enclosing loop or switch immediately:
for (let i = 0; i < a.length; i++) {
if (a[i] === target) break;
}
Labeled break can exit any enclosing labeled statement — not just the nearest loop/switch — which is useful
for breaking out of nested loops from the inner one:
computeSum: for (const row of matrix) {
for (const cell of row) {
if (Number.isNaN(cell)) break computeSum; // jumps past both loops
sum += cell;
}
}
A label is just identifier: placed before any statement; it lives in its own namespace, so it can’t collide with
a variable or function name. break (like continue) cannot cross a function boundary — you cannot label an
outer function and break to it from inside a nested one.
continue
continue skips the rest of the current iteration and moves on to the next one, rather than exiting the loop
entirely:
for (let i = 0; i < data.length; i++) {
if (!data[i]) continue; // skip undefined entries
total += data[i];
}
Like break, continue accepts an optional label to target an outer loop from within a nested one. What "the
next iteration" means depends on the loop type: in a for loop, the increment expression still runs before the
test is re-checked; in a while loop, the test is re-checked directly; in for…of/for…in, the next
value/property is assigned and the loop resumes.
A newline is never allowed between break/continue and its label — automatic semicolon insertion
(see Lexical Structure) would otherwise insert a semicolon right
after the keyword and silently turn a labeled jump into an unlabeled one.
|