Skip to main content

Command Palette

Search for a command to run...

Control Flow in JavaScript: If, Else, and Switch Explained

Published
7 min read

TL;DR

Control flow determines which parts of your program run and when. Use if, if-else, and else if to make boolean decisions. Use switch when you have many exact-match branches (like checking specific values). This article explains each structure, shows clear examples, visualizes the flow with diagrams, and gives practice assignments.

Introduction - Why control flow matters

Computers follow instructions. Control flow is the mechanism that lets your program choose between multiple paths — essentially decision-making inside code.

Real life: if it’s raining, take an umbrella; otherwise don’t. Programs need the same kind of branching logic to react differently based on input, state, or user actions.

What you'll learn:

  • if / if-else / else if syntax and when to use them

  • switch syntax and the role of break

  • How to pick between if-else and switch

  • Common beginner mistakes and best practices

  • Small exercises with solutions

The if statement

Purpose

Use if when you want to run a block of code only when a condition is true.

Syntax

if (condition) {
  // runs when condition is truthy
}

Simple example

const isRaining = true;
if (isRaining) {
  console.log("Take an umbrella");
}

What to explain while writing this section

  • condition is any expression that evaluates to a boolean (or to a value that coerces to boolean).

  • Parentheses around the condition are required. Curly braces for the block are required for multi-line blocks (single statements can omit braces but avoid that for readability).

  • Truthy vs falsy (brief): 0, "", null, undefined, NaN, and false are falsy; everything else is truthy.

Step-by-step trace example

const temp = 30;
if (temp > 25) {
  console.log("It's warm");
}
  1. Evaluate temp > 2530 > 25true.

  2. Enter the if block and run console.log("It's warm").

Common beginner mistakes

  • Using assignment = instead of comparison ==/===.

  • Forgetting braces when adding more statements later.

  • Relying on implicit coercion without understanding truthy/falsy.

The if-else statement

Purpose

Use if-else when you need one behavior for true and a different behavior for false.

Syntax

if (condition) {
  // runs when condition is true
} else {
  // runs when condition is false
}

Example

const age = 17;
if (age >= 18) {
  console.log("You can vote");
} else {
  console.log("You are not eligible to vote yet");
}

Step-by-step execution (trace)

  1. Evaluate age >= 1817 >= 18false.

  2. Skip if block; execute else block.

Pitfalls

  • Putting return or break in one branch and not the other without intention.

  • Overly long else blocks; sometimes better to return early and keep code flatter.

The else if ladder

Purpose

Use else if when you have more than two mutually exclusive branches — e.g., multiple ranges or categories.

Syntax

if (cond1) {
  // ...
} else if (cond2) {
  // ...
} else if (cond3) {
  // ...
} else {
  // fallback
}

Example: grading

const score = 76;
if (score >= 90) {
  console.log("A");
} else if (score >= 75) {
  console.log("B");
} else if (score >= 60) {
  console.log("C");
} else {
  console.log("F");
}

Execution order note

JavaScript evaluates conditions top-to-bottom and stops at the first true condition. That’s why the most specific (or highest priority) checks should come first.

Step-by-step trace for score = 76

  1. score >= 90false.

  2. score >= 75true → print "B" → skip rest.

Common beginner mistakes

  • Ordering conditions wrongly (e.g., checking x > 10 before x > 20).

  • Using many else ifs where a switch (or map) would be cleaner.

The switch statement

Purpose

Use switch for multiple branches based on exact matches of a single expression (often better than a long else if ladder when checking the same variable for equality against many possible values).

Syntax

switch (expression) {
  case value1:
    // code
    break;
  case value2:
    // code
    break;
  default:
    // fallback
}

Example: day of week

const day = 3;
switch (day) {
  case 1:
    console.log("Monday");
    break;
  case 2:
    console.log("Tuesday");
    break;
  case 3:
    console.log("Wednesday");
    break;
  default:
    console.log("Invalid day");
}

Key points about break

  • break exits the switch block. Without break, execution falls through to the next case.

  • Fall-through can be useful intentionally (see consolidated cases below), but it’s a common source of bugs when accidental.

Intentional fall-through example

const v = "banana";
switch (v) {
  case "apple":
  case "banana":
  case "pear":
    console.log("This is a common fruit");
    break;
  default:
    console.log("Other");
}

Here, apple, banana, and pear share the same outcome.

Common beginner mistakes

  • Forgetting break, causing unexpected fall-through.

  • Using complex expressions in case labels — they are matched with strict equality (===-like).

  • Expecting switch to work with ranges — it doesn't (unless you use tricks like true in switch).

Trick: switch(true) for ranges (advanced)

Not recommended for beginners, but useful to know:

const score = 85;
switch (true) {
  case score >= 90:
    console.log("A");
    break;
  case score >= 75:
    console.log("B");
    break;
  default:
    console.log("F");
}

This works because switch compares the expression (here true) to each case expression using strict equality.

When to use switch vs if-else

Quick heuristics

  • Use if/if-else/else if when:

    • Conditions are ranges or complex boolean expressions (e.g., x > 10 && y < 5).

    • You need to evaluate different variables or mixed conditions.

  • Use switch when:

    • You have one expression/variable and many exact-value checks (e.g., menu choices, status codes).

    • Branches are simple and based on equality.

    • You prefer clearer visual grouping of all possible values.

Examples

  • HTTP status code handling: switch (statusCode) { case 200: ... }

  • Small set of commands (e.g., CLI): switch (command) { ... }

  • Ranges like grades or thresholds: prefer if-else.

Readability and maintainability

If your else if ladder is long and each branch checks the same variable for equality, switch is clearer. Conversely, prefer if-else for flexible conditions.

Common pitfalls and best practices

Pitfalls

  • Using = instead of === or ==. Always use === unless you have a specific reason to coerce.

  • Forgetting break in switch.

  • Over-nesting conditionals — deeply nested ifs are hard to read.

  • Long else if ladders that check different variables — refactor into separate functions or use a Map.

Best practices

  • Prefer explicit comparisons (age >= 18) over relying on truthy/falsy for user-facing logic.

  • Return early to keep code flat:

    function handle(x) {
      if (!x) return;
      // rest of logic
    }
    
  • Use switch for many exact-value checks; use if for boolean expressions and ranges.

  • Comment intentional fall-through in switches clearly:

    case 1:
      // fall-through intentional
    case 2:
      doSomething();
      break;
    

Quick reference (cheat-sheet)

  • if (cond) {...} — run block when cond truthy.

  • if (cond) {...} else {...} — choose between two branches.

  • if (a) {...} else if (b) {...} else {...} — chain multiple checks.

  • switch (expr) { case v: ... break; default: ... } — best for many exact matches.

  • Use === for equality. Use break to avoid fall-through. Comment intentional fall-through.

Conclusion and next steps

Control flow is foundational. Once you're comfortable with if/else and switch, practice by:

  • Adding input validation (bad inputs → show friendly messages)

  • Rewriting switch logic as a Map lookup when values map to functions or text

  • Avoiding deep nesting: refactor with functions or early returns

  • Try small projects like a CLI menu, form validation, or a simple rule-based game

Appendix — Extra examples & refactor ideas

Using a Map instead of switch (refactor)

const dayMap = new Map([
  [0, "Sunday"],
  [1, "Monday"],
  [2, "Tuesday"],
  [3, "Wednesday"],
  [4, "Thursday"],
  [5, "Friday"],
  [6, "Saturday"]
]);

function dayName(dayNumber) {
  return dayMap.get(dayNumber) ?? "Invalid day number";
}

This scales better when you need to store extra metadata or functions per case.

Early-return pattern to reduce nesting

function process(user) {
  if (!user) return "No user";
  if (!user.active) return "Inactive user";
  // continue with core logic
}