Control flow#

Control flow is how the operator’s program decides what runs next. JavaScript ships if / else, switch, for / for...of / for...in / while / do...while, break / continue / return / throw. There is no match keyword (proposal still in flight); object dispatch and switch cover the common cases.

This page is the day-to-day reference. For values these branch on, see Types. For throw / try, see Errors.

Conditional#

if / else if / else evaluates branches top-to-bottom and takes the first whose condition is truthy.

        flowchart TD
    A([start]) --> B{x > 0?}
    B -->|true| C[positive]
    B -->|false| D{x === 0?}
    D -->|true| E[zero]
    D -->|false| F[negative]
    C --> Z([end])
    E --> Z
    F --> Z
    
if (x > 0) {
  positive();
} else if (x === 0) {
  zero();
} else {
  negative();
}

The ternary cond ? a : b is the expression form.

const label = n % 2 === 0 ? "even" : "odd";

Useful when the branch fits one expression.

const z = a > 0 ? a : -a;

Switch#

switch dispatches on a value with === semantics. Cases fall through to the next case unless write break.

        flowchart TD
    A([switch cmd]) --> B{"start | go"}
    B -->|yes| C[run]
    B -->|no| D{stop}
    D -->|yes| E[halt]
    D -->|no| F[default: throw]
    C --> Z([break out])
    E --> Z
    
switch (cmd) {
  case "start":
  case "go":
    run();
    break;
  case "stop":
    halt();
    break;
  default:
    throw new Error(`unknown: ${cmd}`);
}

For type-style dispatch, the operator increasingly reaches for an object-as-table pattern rather than switch.

const handlers = {
  start: run,
  stop:  halt,
};
(handlers[cmd] ?? unknown)(cmd);

Loops#

Four forms. Pick by what the operator is iterating.

        flowchart TD
    A([init]) --> B{cond?}
    B -->|true| C[body]
    C --> D[step]
    D --> B
    B -->|false| E([end])
    

For (C-style)#

Init, condition, step. Best when the operator needs a numeric counter or a non-iterable walk.

for (let i = 0; i < 10; i++) {
  console.log(i);
}

For…of#

Walks any iterable (arrays, strings, Map, Set, generators, anything implementing Symbol.iterator). The operator’s default for collection iteration.

for (const item of items) handle(item);

for (const ch of "operator") console.log(ch);

for (const [k, v] of Object.entries(obj)) console.log(k, v);

For…in#

Walks enumerable string keys of an object, including inherited ones. Rarely the right tool; Object.keys / Object.entries plus for...of is clearer and avoids inherited keys.

for (const k in obj) {
  if (Object.hasOwn(obj, k)) console.log(k, obj[k]);
}

While and do…while#

while tests first; do...while tests after the body has run once.

while (queue.length) handle(queue.shift());

do {
  line = await readLine();
  process(line);
} while (line !== "quit");

Jumps#

Keyword

Effect

break

Exit the innermost loop or switch. With a label, break out of a labelled outer loop.

continue

Skip the rest of the loop body, jump to the next iteration.

return

Exit the current function with a value (default undefined).

throw

Raise an exception (see Errors).

outer: for (const row of rows) {
  for (const cell of row) {
    if (cell === target) break outer;     // jump out of both loops
  }
}

for (const line of lines) {
  if (line.startsWith("#")) continue;     // skip comments
  process(line);
}

References#