Tutorials

JavaScript ES6 Control Flow

Key Takeaways

  • Use if...else for conditions, ranges, and compound rules; use switch when comparing one expression against several discrete values.
  • An if condition uses truthiness. An else if chain stops testing conditions after the first truthy result.
  • A switch evaluates its input expression once and matches cases using strict equality.
  • Missing break statements can cause fall-through. A final else and a default clause are optional—not mandatory safety features.

Use JavaScript if...else statements to choose a branch based on conditions, and switch to select among discrete values. Choose whichever makes your decision rules easiest to understand—not whichever uses fewer lines.

Run each example independently in your browser's developer console. If the declarations are unfamiliar, start with JavaScript var, let, and const.

JavaScript if: Run Code When a Condition Is Truthy

An if statement runs its body when its condition is truthy—meaning JavaScript treats the result as true in a Boolean context. The condition can be any expression; it doesn't have to be an explicit comparison.

const x = 0;

if (x === 0) {
  console.log("x is zero!");
}

// Logs: x is zero!

Prefer === when you want equality without type conversion: 1 === "1" is false. For objects, strict equality compares identity, not matching properties: two separately created objects aren't strictly equal just because their contents look identical. See JavaScript operators for more comparison examples.

Truthiness Is Different From Equality

Falsy primitive values are false, undefined, null, 0, -0, 0n, NaN, and the empty string. Nonempty strings—including "false" and "0"—are truthy, as are empty arrays and ordinary objects. The legacy browser value document.all is an exceptional falsy object.

const count = 0;

if (count) {
  console.log("count is truthy");
}

if (count === 0) {
  console.log("count is zero");
}

// Logs only: count is zero

Use an explicit comparison when zero is meaningful. A truthiness check answers a different question from “is this value zero?”

JavaScript if...else: Choose Between Two Branches

The else body runs when the if condition is falsy. Assuming the condition evaluates normally, exactly one of these two bodies executes.

const x = 1;

if (x === 0) {
  console.log("x is zero!");
} else {
  console.log("x is not zero!");
}

// Logs: x is not zero!

Use braces consistently, even for one statement. They make branch boundaries visible and help avoid confusing nested if/else associations.

else if: Test Conditions in Order

The conventional spelling is else if, with a space. It isn't a single keyword: it's another if statement inside an else branch. Conditions are tested in order until one is truthy; later conditions and their bodies are then skipped.

const x = 2;

if (x === 0) {
  console.log("x is zero!");
} else if (x === 1) {
  console.log("x is one!");
} else {
  console.log("x is neither zero nor one!");
}

// Logs: x is neither zero nor one!
First condition
  |-- truthy --> first body --> after chain
  `-- falsy --> next condition
                  |-- truthy --> next body --> after chain
                  `-- falsy --> final else, if present --> after chain

A final else is optional. Include it when every remaining input needs handling; omit it when doing nothing is the intended outcome. Unlike an else if chain, separate consecutive if statements each test their own condition, so multiple bodies can run.

JavaScript switch: Match One Expression Against Values

A switch evaluates its input expression once, then looks for the first matching case using strict equality—the comparison used by ===. Execution begins at the matching label.

const a = 0;

switch (a + 1) {
  case 0:
    console.log("hello");
    break;
  case 1:
  case 2:
    console.log("goodbye");
    break;
  case 3:
    console.log("adios");
    break;
  default:
    console.log("no cases match");
}

// Logs: goodbye

The adjacent case 1: and case 2: labels intentionally share a body. Either value reaches the same log statement, followed by a break.

Switch Matching Doesn't Convert Types

switch (1) {
  case "1":
    console.log("string one");
    break;
  default:
    console.log("No match: the types differ");
}

// Logs: No match: the types differ

The number 1 doesn't match the string "1". If your input can arrive in different types, normalize or validate it before branching rather than expecting switch to convert it.

Case Expressions Can Call Functions

A case doesn't have to be a constant. Its expression can call a function, and the returned value participates in matching. Case expressions are evaluated only as needed to find a match; later expressions aren't evaluated after a match is found, even if execution falls through their labels.

function expectedStatus() {
  console.log("Checking case");
  return "ready";
}

switch ("ready") {
  case expectedStatus():
    console.log("Start");
    break;
  default:
    console.log("Wait");
}

// Logs:
// Checking case
// Start

Prefer simple case expressions. Putting side effects—operations that change state or produce output—inside matching expressions makes execution harder to follow.

The break Keyword and Switch Fall-Through

Fall-through means execution continues into the statements under later labels without requiring another match. Adjacent labels sharing a body are a useful intentional example; forgetting break after a populated body is a common mistake.

const step = 1;

switch (step) {
  case 1:
    console.log("first");
    // Missing break: execution falls through.
  case 2:
    console.log("second");
    break;
}

console.log("after switch");

// Logs:
// first
// second
// after switch

Add break; after the first log to prevent the second log. If continuing into another populated body is intentional, leave an explicit fall-through comment.

An unlabeled break exits the innermost enclosing loop or switch. In this example it exits the switch—not the surrounding function, and not an outer loop containing that switch. Use return when you intend to leave a function; see JavaScript functions and JavaScript loops for the surrounding control flow.

The default Case: An Optional Fallback

If no case matches, execution starts at default, if present. With no match and no default, no case body runs; execution continues after the switch. This is defined behavior, not “undefined behavior.”

switch (99) {
  case 1:
    console.log("one");
    break;
}

console.log("after switch");

// Logs only: after switch

Include a fallback when unknown values need a response, such as an error or a user-facing message. Prefer placing default last for readability; it isn't required to be last, and execution can also reach it through fall-through.

switch vs if...else: Choose the Clearer Decision Model

DecisionRecommended starting pointReason
One optional actionifNo fallback is needed.
Ranges or compound conditionsif...elseThe rules read naturally as conditions.
One expression, several discrete valuesswitchThe possible values stay together.
Keys mapped directly to resultsA lookup object or MapRepresent the relationship as data.

Neither branching statement is inherently more maintainable. For the earlier switch, this if...else version is equally valid and computes the selected value once:

const a = 0;
const value = a + 1;

if (value === 0) {
  console.log("hello");
} else if (value === 1 || value === 2) {
  console.log("goodbye");
} else if (value === 3) {
  console.log("adios");
} else {
  console.log("no cases match");
}

// Logs: goodbye

For ranges, start with if...else. Order your conditions deliberately: the first truthy condition wins.

const score = 82;

if (score >= 90) {
  console.log("Excellent");
} else if (score >= 70) {
  console.log("Pass");
} else {
  console.log("Try again");
}

// Logs: Pass

Don't choose switch because you expect a speedup. Start with readable code; if profiling identifies a branching bottleneck, benchmark equivalent implementations in your actual runtime. Our JavaScript performance tips cover the broader optimization workflow.

Use a Lookup When the Branches Only Select Data

If every branch merely selects a value, consider an object or Map instead. Here, Map.has() checks whether the key exists and Map.get() retrieves its value.

const messages = new Map([
  [0, "hello"],
  [1, "goodbye"],
  [2, "goodbye"],
  [3, "adios"],
]);
const value = 2;

if (messages.has(value)) {
  console.log(messages.get(value));
} else {
  console.log("no cases match");
}

// Logs: goodbye

A missing key makes Map.get() return undefined. Checking has() distinguishes an absent key from a key deliberately mapped to undefined.

Common Mistakes

  • Assignment instead of comparison: if (x = 0) assigns zero; it doesn't test equality. Use if (x === 0) when that's your question.
  • Broad conditions before specific ones: a passing condition skips the remaining else if checks. Test overlapping rules at their boundaries.
  • Assuming case labels create scopes: they don't. Wrap a case body in braces when it needs its own let or const bindings; otherwise, duplicate declarations across cases can produce a syntax error.
  • Testing only matching values: also test unmatched inputs, unexpected types, zero, and each intended shared branch.

FAQ

Does a JavaScript switch use strict equality?

Yes. A switch compares its input with case-expression results using strict equality, so 1 doesn't match "1". Objects match only when both sides refer to the same object.

What happens when a break is omitted from a JavaScript switch?

Unless another statement transfers control, execution continues through subsequent case bodies until it reaches a break or the switch ends. Those later cases don't have to match; this is called fall-through.

When should I use switch instead of if...else?

Start with switch when one expression is compared against several discrete values. Prefer if...else for ranges, compound conditions, or checks involving different expressions, then choose whichever is clearer.

Can a JavaScript case expression call a function?

Yes. A case expression can call a function, and its returned value is used for matching. Case expressions are evaluated only as needed until a match is found, so avoid relying on their side effects.

Is a default case required in JavaScript?

No. If no case matches and there's no default, the switch executes no case body and execution continues after it. Add default when unmatched values need explicit handling.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me