JavaScript Conditional Statements: if, else & switch Guide
Learn every JavaScript conditional pattern with practical examples. Covers if, else, else if, switch, ternary operator, truthy and falsy values, comparison operators, nested conditions, and common mistakes.
Conditional statements let your program make decisions. Instead of running every line of code from top to bottom, conditionals evaluate a condition and execute different code branches based on whether that condition is true or false. JavaScript provides several ways to express conditions: if/else statements, the switch statement, and the ternary operator.
This guide covers every conditional pattern you need, from basic if statements to advanced technique combinations, with code examples and comparison tables.
The if Statement
The if statement is the simplest conditional. It runs a block of code only when the condition evaluates to true.
const temperature = 35;
if (temperature > 30) {
console.log("It's hot outside!");
}
// "It's hot outside!"if...else
Add an else block to handle the case when the condition is false:
const age = 16;
if (age >= 18) {
console.log("You can vote");
} else {
console.log("You cannot vote yet");
}
// "You cannot vote yet"if...else if...else
Chain multiple conditions with else if:
const score = 82;
let grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else if (score >= 60) {
grade = "D";
} else {
grade = "F";
}
console.log(`Score ${score} = Grade ${grade}`); // "Score 82 = Grade B"Comparison Operators
Conditionals depend on comparison operators to produce true or false values.
| Operator | Meaning | Example | Result |
|---|---|---|---|
=== | Strict equality (type + value) | 5 === 5 | true |
!== | Strict inequality | 5 !== "5" | true |
== | Loose equality (coerces type) | 5 == "5" | true |
!= | Loose inequality | 5 != "5" | false |
> | Greater than | 10 > 5 | true |
< | Less than | 3 < 7 | true |
>= | Greater than or equal | 5 >= 5 | true |
<= | Less than or equal | 4 <= 3 | false |
Always prefer strict equality (=== and !==) to avoid unexpected type coercion.
Truthy and Falsy Values
JavaScript conditions do not require explicit booleans. Any value can act as a condition because JavaScript coerces values to true (truthy) or false (falsy).
// Falsy values: false, 0, -0, "", null, undefined, NaN
if (0) {
console.log("This never runs");
}
// Truthy: everything else
if ("hello") {
console.log("Non-empty strings are truthy"); // This runs
}
if ([]) {
console.log("Empty arrays are truthy"); // This runs!
}
if ({}) {
console.log("Empty objects are truthy"); // This runs!
}| Value | Truthy/Falsy | Notes |
|---|---|---|
false | Falsy | Boolean false |
0, -0 | Falsy | Zero |
"" (empty string) | Falsy | Empty string |
null | Falsy | Intentional absence |
undefined | Falsy | Uninitialized variable |
NaN | Falsy | Not a number |
| Everything else | Truthy | Including [], {}, "0", " " |
Logical Operators
Combine multiple conditions using logical operators:
const age = 25;
const hasLicense = true;
const hasInsurance = true;
// AND: both must be true
if (age >= 18 && hasLicense) {
console.log("Eligible to drive");
}
// OR: at least one must be true
if (hasLicense || hasInsurance) {
console.log("Has at least one form of coverage");
}
// NOT: inverts the value
if (!hasInsurance) {
console.log("Needs insurance");
} else {
console.log("Insurance confirmed");
}Short-Circuit Evaluation
JavaScript stops evaluating logical expressions as soon as the result is determined:
const user = null;
// AND short-circuits: if left is falsy, returns it without evaluating right
const name = user && user.name; // null (does not throw)
// OR short-circuits: if left is truthy, returns it
const displayName = name || "Guest"; // "Guest"
console.log(displayName); // "Guest"The switch Statement
The switch statement compares a value against multiple cases using strict equality. It is cleaner than long else if chains when you are testing one variable against many possible values.
const dayNumber = new Date().getDay();
let dayName;
switch (dayNumber) {
case 0:
dayName = "Sunday";
break;
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
case 6:
dayName = "Saturday";
break;
default:
dayName = "Unknown";
}
console.log(`Today is ${dayName}`);Grouping Cases (Fall-Through)
const fruit = "apple";
switch (fruit) {
case "apple":
case "pear":
case "cherry":
console.log("This is a temperate fruit");
break;
case "mango":
case "pineapple":
case "banana":
console.log("This is a tropical fruit");
break;
default:
console.log("Unknown fruit type");
}
// "This is a temperate fruit"The Ternary Operator
The ternary operator provides a compact way to write simple if/else expressions:
const age = 20;
const status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"
// Useful for inline assignments
const greeting = (hour) => hour < 12 ? "Good morning" : "Good afternoon";
console.log(greeting(9)); // "Good morning"
console.log(greeting(15)); // "Good afternoon"Avoid chaining ternary operators beyond two levels. Complex ternaries hurt readability; use if/else or switch instead.
Nullish Coalescing and Optional Chaining
Nullish Coalescing (??)
The ?? operator returns the right-hand value only when the left is null or undefined (not for other falsy values like 0 or ""):
const userSettings = {
theme: "",
fontSize: 0,
notifications: null
};
// || treats 0 and "" as falsy -> returns defaults incorrectly
const fontSize = userSettings.fontSize || 16; // 16 (wrong!)
const theme = userSettings.theme || "dark"; // "dark" (wrong!)
// ?? only triggers on null/undefined
const fontSizeCorrect = userSettings.fontSize ?? 16; // 0 (correct!)
const themeCorrect = userSettings.theme ?? "dark"; // "" (correct!)
const notifications = userSettings.notifications ?? true; // true (correct)Optional Chaining (?.)
Safely access deeply nested properties without explicit null checks:
const user = {
profile: {
address: {
city: "New York"
}
}
};
// Without optional chaining
const city = user && user.profile && user.profile.address
? user.profile.address.city
: "Unknown";
// With optional chaining
const cityClean = user?.profile?.address?.city ?? "Unknown";
console.log(cityClean); // "New York"
// Works with methods too
const length = user?.profile?.getName?.(); // undefined (no error)Guard Clauses (Early Returns)
Guard clauses reduce nesting by handling edge cases first and returning early:
// Deeply nested version (hard to read)
function processOrder(order) {
if (order) {
if (order.items.length > 0) {
if (order.paymentVerified) {
// Process the order
return { status: "processed", orderId: order.id };
} else {
return { status: "error", message: "Payment not verified" };
}
} else {
return { status: "error", message: "No items in order" };
}
} else {
return { status: "error", message: "No order provided" };
}
}
// Guard clause version (clean and flat)
function processOrderClean(order) {
if (!order) {
return { status: "error", message: "No order provided" };
}
if (order.items.length === 0) {
return { status: "error", message: "No items in order" };
}
if (!order.paymentVerified) {
return { status: "error", message: "Payment not verified" };
}
return { status: "processed", orderId: order.id };
}Common Mistakes
Missing break in switch
const color = "red";
// Bug: fall-through without break
switch (color) {
case "red":
console.log("Stop");
case "yellow":
console.log("Caution");
case "green":
console.log("Go");
}
// Prints: "Stop", "Caution", "Go" (all three!)
// Fix: add break after each case
switch (color) {
case "red":
console.log("Stop");
break;
case "yellow":
console.log("Caution");
break;
case "green":
console.log("Go");
break;
}
// Prints: "Stop" (only)Using = Instead of ===
const x = 5;
// Bug: assignment, not comparison
if (x = 10) {
console.log("This always runs because assignment returns 10 (truthy)");
}
// Fix: use ===
if (x === 10) {
console.log("This checks equality correctly");
}Best Practices
- Use strict equality (
===) to avoid type coercion surprises. - Keep conditions simple. Extract complex conditions into named variables for readability.
- Prefer guard clauses over deeply nested if/else blocks.
- Use switch for 3+ cases against the same variable; use if/else for range checks and complex conditions.
- Avoid negated conditions in else blocks.
if (!isValid) { handleError() } else { proceed() }reads worse thanif (isValid) { proceed() } else { handleError() }.
Rune AI
Key Insights
- if/else is the foundation of branching: use it for range checks, complex conditions, and general purpose decision-making.
- switch simplifies multi-value comparisons: always include
breakstatements to prevent unintended fall-through behavior. - Truthy/falsy coercion is automatic: know the six falsy values to avoid surprises in your conditions.
- Nullish coalescing preserves valid falsy values: use
??instead of||when0,"", orfalseare legitimate defaults. - Guard clauses flatten nested code: handle errors and edge cases early with returns, keeping the main logic at the top indentation level.
Frequently Asked Questions
When should I use switch instead of if/else?
What is the difference between == and === in JavaScript?
How do truthy and falsy values affect conditionals?
What is the nullish coalescing operator?
How deep should I nest if statements?
Conclusion
JavaScript conditionals give you fine-grained control over program flow. The if/else statement handles most branching logic, the switch statement simplifies multi-option comparisons, and the ternary operator provides inline single-expression decisions. Combined with truthy/falsy evaluation, nullish coalescing, and optional chaining, you can write clean conditional logic that handles edge cases without excessive nesting. Master guard clauses for flat, readable code, and always use strict equality to avoid silent bugs.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.