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.

JavaScriptbeginner
16 min read

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.

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
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.

OperatorMeaningExampleResult
===Strict equality (type + value)5 === 5true
!==Strict inequality5 !== "5"true
==Loose equality (coerces type)5 == "5"true
!=Loose inequality5 != "5"false
>Greater than10 > 5true
<Less than3 < 7true
>=Greater than or equal5 >= 5true
<=Less than or equal4 <= 3false

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).

javascriptjavascript
// 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!
}
ValueTruthy/FalsyNotes
falseFalsyBoolean false
0, -0FalsyZero
"" (empty string)FalsyEmpty string
nullFalsyIntentional absence
undefinedFalsyUninitialized variable
NaNFalsyNot a number
Everything elseTruthyIncluding [], {}, "0", " "

Logical Operators

Combine multiple conditions using logical operators:

javascriptjavascript
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:

javascriptjavascript
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.

javascriptjavascript
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)

javascriptjavascript
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:

javascriptjavascript
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 ""):

javascriptjavascript
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:

javascriptjavascript
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:

javascriptjavascript
// 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

javascriptjavascript
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 ===

javascriptjavascript
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

  1. Use strict equality (===) to avoid type coercion surprises.
  2. Keep conditions simple. Extract complex conditions into named variables for readability.
  3. Prefer guard clauses over deeply nested if/else blocks.
  4. Use switch for 3+ cases against the same variable; use if/else for range checks and complex conditions.
  5. Avoid negated conditions in else blocks. if (!isValid) { handleError() } else { proceed() } reads worse than if (isValid) { proceed() } else { handleError() }.
Rune AI

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 break statements 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 || when 0, "", or false are legitimate defaults.
  • Guard clauses flatten nested code: handle errors and edge cases early with returns, keeping the main logic at the top indentation level.
RunePowered by Rune AI

Frequently Asked Questions

When should I use switch instead of if/else?

Use switch when you are comparing one value against three or more fixed options (like days of the week, status codes, or user roles). Switch is more readable than long else if chains for these cases. Use if/else when your conditions involve ranges, complex expressions, or different variables.

What is the difference between == and === in JavaScript?

Double equals (`==`) performs type coercion before comparing, so `5 == "5"` is `true`. Triple equals (`===`) checks both type and value, so `5 === "5"` is `false`. Always use `===` unless you have a specific reason to allow type coercion, because loose equality produces counterintuitive results like `"" == 0` being `true`.

How do truthy and falsy values affect conditionals?

JavaScript automatically converts any value to a boolean in conditional contexts. Six values are falsy: `false`, `0`, `""`, `null`, `undefined`, and `NaN`. Everything else is truthy, including empty arrays and objects. This means `if (myArray.length)` works because `0` is falsy and any positive number is truthy.

What is the nullish coalescing operator?

The nullish coalescing operator (`??`) returns the right-hand operand when the left-hand operand is `null` or `undefined`, but not for other falsy values. This differs from the OR operator (`||`), which returns the right-hand operand for any falsy value including `0`, `""`, and `false`. Use `??` when you need to preserve legitimate falsy values as valid defaults.

How deep should I nest if statements?

Keep nesting to two levels maximum. Beyond that, use guard clauses (early returns), extract conditions into helper functions, or restructure with switch statements. Deep nesting makes code harder to read, test, and maintain. Most deeply nested code can be flattened with early returns.

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.