Control Flow in JavaScript: If, Else, and Switch Explained
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 ifsyntax and when to use themswitchsyntax and the role ofbreakHow to pick between
if-elseandswitchCommon 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
conditionis 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, andfalseare falsy; everything else is truthy.
Step-by-step trace example
const temp = 30;
if (temp > 25) {
console.log("It's warm");
}
Evaluate
temp > 25→30 > 25→true.Enter the
ifblock and runconsole.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)
Evaluate
age >= 18→17 >= 18→false.Skip
ifblock; executeelseblock.
Pitfalls
Putting return or break in one branch and not the other without intention.
Overly long
elseblocks; sometimes better toreturnearly 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
score >= 90→false.score >= 75→true→ print"B"→ skip rest.
Common beginner mistakes
Ordering conditions wrongly (e.g., checking
x > 10beforex > 20).Using many
else ifs where aswitch(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
breakexits theswitchblock. Withoutbreak, 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
caselabels — they are matched with strict equality (===-like).Expecting
switchto work with ranges — it doesn't (unless you use tricks liketruein 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 ifwhen:Conditions are ranges or complex boolean expressions (e.g.,
x > 10 && y < 5).You need to evaluate different variables or mixed conditions.
Use
switchwhen: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
breakinswitch.Over-nesting conditionals — deeply nested
ifs are hard to read.Long
else ifladders that check different variables — refactor into separate functions or use aMap.
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
switchfor many exact-value checks; useiffor 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 whencondtruthy.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. Usebreakto 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
switchlogic as aMaplookup when values map to functions or textAvoiding 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
}

