The if chain is JavaScript's general-purpose decision tool, but two alternatives express specific shapes more cleanly: switch for dispatching one variable to many discrete values, and the ternary ? : for one-line "this or that" expressions. This lesson teaches both, the traps each one carries, and when to fall back on a plain if/else.
switch — one variable, many cases
When you're checking the same variable against several specific values, a long if/else if chain reads as visual noise:
if (method === "GET") runReadTest();
else if (method === "POST") runCreateTest();
else if (method === "PUT") runUpdateTest();
else if (method === "DELETE") runDeleteTest();
else throw new Error("Unknown method");switch is the same logic, but the branching variable is named once at the top:
const method = "POST";
switch (method) {
case "GET":
console.log("Run read test");
break;
case "POST":
console.log("Run create test");
break;
case "PUT":
console.log("Run update test");
break;
case "DELETE":
console.log("Run delete test");
break;
default:
console.log("Unknown method");
}Output:
Run create test
Each piece does one job. switch (method) says "compare method against the cases below." case "POST": is the value to match. The block runs until break. default runs if no case matched — the equivalent of the trailing else.
switch uses strict equality (===) under the hood, so the type-coercion warnings from the previous lesson don't apply here.
The break trap
The single most common switch bug is forgetting break. Without it, JavaScript "falls through" — once a case matches, every block below it runs too, until it hits a break or the end.
Buggy:
const severity = "high";
switch (severity) {
case "critical":
console.log("Page on-call");
case "high":
console.log("Slack the team");
case "medium":
console.log("Email the lead");
case "low":
console.log("Add to dashboard");
}Output (with no breaks):
Slack the team
Email the lead
Add to dashboard
Three escalations for one bug. Adding break after each case fixes it:
switch (severity) {
case "critical":
console.log("Page on-call");
break;
case "high":
console.log("Slack the team");
break;
case "medium":
console.log("Email the lead");
break;
case "low":
console.log("Add to dashboard");
break;
}Output:
Slack the team
Fall-through can be intentional — multiple cases sharing the same body — but use it with a comment so the next reader knows it wasn't an oversight:
switch (statusCode) {
case 200:
case 201:
case 204:
// intentional fall-through — all 2xx success codes share one branch
console.log("✅ success");
break;
default:
console.log("❌ failure");
}The ternary operator
When a decision boils down to "this value if true, that value if false," the ternary ? : operator condenses an entire if/else into one expression.
Syntax:
condition ? valueIfTrue : valueIfFalse
Real example:
const passed = true;
const result = passed ? "✅ PASS" : "❌ FAIL";
console.log(result);Output:
✅ PASS
The ternary is an expression — it evaluates to a value. That's its main superpower over if/else: you can use it on the right-hand side of an assignment, inside a template literal, as a function argument. Picking a base URL based on environment is the canonical case:
const isProduction = false;
const baseUrl = isProduction
? "https://api.prod.com"
: "https://api.staging.com";
console.log(`Running tests against ${baseUrl}`);Output:
Running tests against https://api.staging.com
Don't nest ternaries
Ternaries shine for simple two-way picks. The temptation, once you know about them, is to chain them for three- or four-way decisions:
// Don't do this
const label =
statusCode < 300 ? "ok"
: statusCode < 400 ? "redirect"
: statusCode < 500 ? "client error"
: "server error";That works. It also takes longer to read than an if/else chain, hides the structure, and is hostile to a debugger or breakpoint. Once you need more than one branch, switch to if/else if (or switch):
let label;
if (statusCode < 300) label = "ok";
else if (statusCode < 400) label = "redirect";
else if (statusCode < 500) label = "client error";
else label = "server error";Same logic, far easier to add a new branch to in six months without rewriting the whole expression.
Picking the right tool
if/else vs switch vs ternary — when to use each
if / else
Default tool — use when nothing else fits
Multiple distinct conditions
Different variables in each branch
Unbounded ranges (>= 200, < 300)
switch
One variable matched against many values
Clearer than 5+ else if branches
Watch out for missing break
Strict equality only
ternary ? :
One-line picks: const x = cond ? a : b
Inside template literals, JSX, args
Never nest more than once
Two outcomes only
⚠️ Common mistakes
- Forgetting
break. The fall-through bug is silent — the script runs, prints multiple lines, and looks plausible. Get into the habit of addingbreakimmediately after writing eachcase, before the body. Linters (ESLint'sno-fallthroughrule) catch most cases automatically. - Using ternary for branches that need statements.
cond ? doA() : doB()is technically legal, but you're calling functions for their side effects, not their return value. That reads worse thanif (cond) doA(); else doB();. Reserve the ternary for when you actually want the value. - Nested ternaries. Once. Maybe. Never twice. If you find yourself chaining
?:two or more times, the resulting code looks like a ransom note. Useif/elseand your future self will thank you.
🎯 Practice task
Build a notification router. 15-20 minutes.
-
In your
js-for-qafolder, createnotify.js. -
Declare
const severity = "high";. -
Use a
switchstatement to print:"critical"→"Page on-call""high"→"Slack the team""medium"→"Email the lead""low"→"Add to dashboard"- any other →
"Unknown severity"
-
Add
breakto each case. Run withnode notify.jsfor several severities and confirm only one line prints each time. -
Below the switch, add one line using the ternary operator:
const channel = severity === "critical" ? "pagerduty" : "slack"; console.log(`Channel: ${channel}`); -
Stretch: deliberately remove the
breakafter the"high"case. Run again — observe which lines now print. Write a one-line comment in the file explaining what fall-through is and why you should have caught this on review.
The next lesson covers loops — running the same code over many test cases, retrying on failure, processing arrays of API results.