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.