Skip to main content

🔀 Lesson 5: Conditionals

Teach your programs to make decisions — run different code depending on whether conditions are true or false.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Write if, else if, and else statements to control program flow
  • Use switch statements for multi-branch decisions
  • Write concise conditions with the ternary operator (? :)
  • Nest conditionals and understand when to avoid deep nesting
  • Combine comparisons and logical operators in real conditions
  • Apply guard clauses for cleaner, more readable code

Estimated Time: 45 minutes

Project: Build a grade calculator that assigns letter grades

📑 In This Lesson

Introduction

So far, your programs run every line of code from top to bottom, every time. But real programs need to make decisions: show a welcome message if the user is logged in, display an error if the password is wrong, apply a discount if the cart total is over $50.

Conditionals let you run different blocks of code depending on whether a condition is true or false. They're the first step toward making your programs truly interactive.

graph TD A["Start"] --> B{"Is condition true?"} B -->|Yes| C["Run this code"] B -->|No| D["Skip it or run alternative"] C --> E["Continue program"] D --> E style B fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style C fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b style D fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b

if / else Statements

The if statement is the most fundamental conditional. It evaluates a condition and runs a block of code only when that condition is true.

Basic if


let temperature = 95;

if (temperature > 90) {
    console.log("It's really hot outside! Stay hydrated. 🔥");
}
// This runs because 95 > 90 is true
                

if / else

Add an else block to handle the case when the condition is false.


let age = 16;

if (age >= 18) {
    console.log("You can vote!");
} else {
    console.log("You're not old enough to vote yet.");
}
// Output: "You're not old enough to vote yet."
                
graph TD A["age = 16"] --> B{"age >= 18?"} B -->|Yes| C["'You can vote!'"] B -->|No| D["'Not old enough yet.'"] style B fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style C fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b style D fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b

Multiple Conditions with Logical Operators

Remember && (AND) and || (OR) from the last lesson? Here's where they shine.


let age = 25;
let hasTicket = true;

if (age >= 18 && hasTicket) {
    console.log("Welcome to the concert!");
} else {
    console.log("Sorry, you can't enter.");
}

// Using OR
let isWeekend = true;
let isHoliday = false;

if (isWeekend || isHoliday) {
    console.log("No work today! 🎉");
} else {
    console.log("Time to get to work.");
}
                

⚠️ Don't Forget the Curly Braces

You can write a single-line if without braces, but don't. It's a common source of bugs. Always use braces — even for one-liners.


// ❌ Avoid — easy to break when adding lines
if (loggedIn) console.log("Welcome");

// ✅ Always use braces
if (loggedIn) {
    console.log("Welcome");
}
                    

else if Chains

When you have more than two possible outcomes, chain else if blocks. JavaScript checks each condition in order and runs the first one that's true.


let score = 82;

if (score >= 90) {
    console.log("Grade: A");
} else if (score >= 80) {
    console.log("Grade: B");
} else if (score >= 70) {
    console.log("Grade: C");
} else if (score >= 60) {
    console.log("Grade: D");
} else {
    console.log("Grade: F");
}
// Output: "Grade: B"
                

💡 Order Matters!

Conditions are checked top to bottom. Once a condition is true, its block runs and all remaining else-if/else blocks are skipped. That's why you check the highest grade first — a score of 95 would match >= 90, >= 80, and >= 70, but only the first match runs.

Real-World Example: Time-Based Greeting


let hour = new Date().getHours();  // 0–23

if (hour < 12) {
    console.log("Good morning! ☀️");
} else if (hour < 17) {
    console.log("Good afternoon! 🌤️");
} else if (hour < 21) {
    console.log("Good evening! 🌅");
} else {
    console.log("Good night! 🌙");
}
                
graph TD A["Get current hour"] --> B{"hour < 12?"} B -->|Yes| C["Good morning ☀️"] B -->|No| D{"hour < 17?"} D -->|Yes| E["Good afternoon 🌤️"] D -->|No| F{"hour < 21?"} F -->|Yes| G["Good evening 🌅"] F -->|No| H["Good night 🌙"] style B fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style D fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style F fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b

switch Statements

When you're comparing one value against many specific options, a switch statement can be cleaner than a long else if chain.


let day = "Wednesday";

switch (day) {
    case "Monday":
        console.log("Start of the work week 😩");
        break;
    case "Tuesday":
    case "Wednesday":
    case "Thursday":
        console.log("Midweek grind 💪");
        break;
    case "Friday":
        console.log("TGIF! 🎉");
        break;
    case "Saturday":
    case "Sunday":
        console.log("Weekend vibes! 🏖️");
        break;
    default:
        console.log("That's not a valid day.");
}
// Output: "Midweek grind 💪"
                

How switch Works

  1. JavaScript evaluates the expression in switch()
  2. It compares the result to each case using strict equality (===)
  3. When a match is found, that block runs
  4. break exits the switch — without it, execution "falls through" to the next case!
  5. default runs if no case matches (like else)

❌ Don't Forget break!

Without break, JavaScript falls through to the next case. This is intentional in the Tuesday/Wednesday/Thursday example above (they share the same output), but it's a common bug when unintended.


// ❌ Bug! Missing break causes fall-through
let fruit = "apple";

switch (fruit) {
    case "apple":
        console.log("Apples are $1.50/lb");
        // No break! Falls through to next case!
    case "banana":
        console.log("Bananas are $0.75/lb");
        break;
}
// Output: BOTH lines print!
// "Apples are $1.50/lb"
// "Bananas are $0.75/lb"
                    

When to Use switch vs. if/else

Use switch When Use if/else When
Comparing one value to many exact matches Checking ranges or complex conditions
Each case is a specific value (string, number) Conditions use &&, ||, >, <, etc.
Multiple cases share the same code Conditions are unrelated to each other

The Ternary Operator

The ternary operator (? :) is a compact way to write a simple if/else that produces a value. It's the only JavaScript operator that takes three operands.


// Syntax: condition ? valueIfTrue : valueIfFalse

let age = 20;
let status = age >= 18 ? "adult" : "minor";
console.log(status);  // "adult"

// Equivalent if/else:
// let status;
// if (age >= 18) {
//     status = "adult";
// } else {
//     status = "minor";
// }
                

Common Use Cases


// Assigning a value based on a condition
let score = 85;
let result = score >= 60 ? "Pass" : "Fail";
console.log(result);  // "Pass"

// Inside template literals
let items = 3;
console.log(`You have ${items} item${items === 1 ? "" : "s"} in your cart.`);
// "You have 3 items in your cart."

// Setting a default
let username = "";
let displayName = username ? username : "Guest";
console.log(displayName);  // "Guest" (empty string is falsy)
                

⚠️ Keep Ternaries Simple

Ternaries are great for short, simple conditions. Don't nest them or use them for complex logic — it quickly becomes unreadable.


// ✅ Good — clear and simple
let label = count === 0 ? "empty" : "has items";

// ❌ Bad — nested ternaries are hard to read
let grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "F";
// Use if/else or else-if for this instead!
                    

Nesting & Guard Clauses

You can nest if statements inside other if statements, but deep nesting makes code hard to follow. Guard clauses are a cleaner alternative.

Nested Conditionals (Often Messy)


// ❌ Deeply nested — hard to follow
function processOrder(user, item) {
    if (user) {
        if (user.isLoggedIn) {
            if (item.inStock) {
                if (user.balance >= item.price) {
                    console.log("Order placed!");
                } else {
                    console.log("Insufficient funds.");
                }
            } else {
                console.log("Item out of stock.");
            }
        } else {
            console.log("Please log in.");
        }
    } else {
        console.log("No user found.");
    }
}
                

Guard Clauses (Much Better)

A guard clause checks for problems early and exits, so the main logic stays flat and readable.


// ✅ Guard clauses — flat and readable
function processOrder(user, item) {
    if (!user) {
        console.log("No user found.");
        return;
    }
    if (!user.isLoggedIn) {
        console.log("Please log in.");
        return;
    }
    if (!item.inStock) {
        console.log("Item out of stock.");
        return;
    }
    if (user.balance < item.price) {
        console.log("Insufficient funds.");
        return;
    }

    console.log("Order placed!");
}
                

✅ Pro Tip: Prefer Guard Clauses

Guard clauses handle edge cases and errors first, then let the "happy path" run without nesting. Your code reads like a checklist instead of a maze. This pattern will serve you well as your programs get more complex.

graph TD A["processOrder called"] --> B{"user exists?"} B -->|No| C["❌ No user found"] B -->|Yes| D{"user logged in?"} D -->|No| E["❌ Please log in"] D -->|Yes| F{"item in stock?"} F -->|No| G["❌ Out of stock"] F -->|Yes| H{"balance >= price?"} H -->|No| I["❌ Insufficient funds"] H -->|Yes| J["✅ Order placed!"] style J fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b style C fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style E fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style G fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style I fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b

Truthy/Falsy in Conditions

Remember truthy and falsy values from Lesson 4? They work directly in if conditions — you don't need to compare to true or false explicitly.


let username = "Ray";

// ❌ Unnecessary — comparing to truthy/falsy explicitly
if (username !== "" && username !== null && username !== undefined) {
    console.log(`Hello, ${username}!`);
}

// ✅ Cleaner — truthy check handles all of the above
if (username) {
    console.log(`Hello, ${username}!`);
}
// Works because non-empty strings are truthy
// Empty string, null, and undefined are all falsy
                

// Common patterns using truthy/falsy
let items = [];

if (items.length) {
    console.log(`You have ${items.length} items.`);
} else {
    console.log("Your cart is empty.");
}
// 0 is falsy, so empty array's .length (0) goes to else

let errorMessage = "";

if (errorMessage) {
    console.log(`Error: ${errorMessage}`);
}
// Empty string is falsy, so this won't run
                

💡 When to Be Explicit

Truthy/falsy checks are great for strings and objects, but be careful with numbers. If 0 is a valid value (not an error), a truthy check will incorrectly treat it as "missing." In those cases, check explicitly: if (count !== undefined) instead of if (count).

Hands-on Exercise

🏋️ Exercise: Grade Calculator

Objective: Build a program that takes a numeric score and outputs the letter grade, plus a message for each grade level.

Instructions:


// Grade Calculator

// TODO: Set the student's score
// const studentName = "Alex";
// const score = 78;

// TODO: Use if/else if/else to determine the letter grade:
// 90–100: "A"
// 80–89:  "B"
// 70–79:  "C"
// 60–69:  "D"
// Below 60: "F"

// TODO: Use a switch on the letter grade to set a message:
// A → "Excellent work!"
// B → "Great job!"
// C → "Solid effort, keep improving!"
// D → "You passed, but review the material."
// F → "Let's set up a study plan."

// TODO: Use a ternary to determine pass/fail (60+ is passing)

// TODO: Log the results:
// "Alex scored 78 (C) — Solid effort, keep improving!"
// "Status: Pass"

// BONUS: Add an edge case check — if score is below 0
// or above 100, log "Invalid score" and skip everything else
                    
💡 Hint

Start with the bonus edge-case check as a guard clause. Then use else if chains for the grade ranges (check highest first). Store the letter grade in a variable so you can use it in the switch statement.

✅ Solution

const studentName = "Alex";
const score = 78;

// Guard clause for invalid scores
if (score < 0 || score > 100) {
    console.log("Invalid score. Must be between 0 and 100.");
} else {
    // Determine letter grade
    let letterGrade;

    if (score >= 90) {
        letterGrade = "A";
    } else if (score >= 80) {
        letterGrade = "B";
    } else if (score >= 70) {
        letterGrade = "C";
    } else if (score >= 60) {
        letterGrade = "D";
    } else {
        letterGrade = "F";
    }

    // Determine message using switch
    let message;

    switch (letterGrade) {
        case "A":
            message = "Excellent work!";
            break;
        case "B":
            message = "Great job!";
            break;
        case "C":
            message = "Solid effort, keep improving!";
            break;
        case "D":
            message = "You passed, but review the material.";
            break;
        case "F":
            message = "Let's set up a study plan.";
            break;
    }

    // Ternary for pass/fail
    const status = score >= 60 ? "Pass" : "Fail";

    // Output
    console.log(`${studentName} scored ${score} (${letterGrade}) — ${message}`);
    console.log(`Status: ${status}`);
}
                        

🎯 Quick Quiz

Question 1: What does this code output?

let x = 5;
if (x > 10) {
    console.log("A");
} else if (x > 3) {
    console.log("B");
} else if (x > 1) {
    console.log("C");
} else {
    console.log("D");
}

Question 2: What happens if you forget break in a switch case?

Question 3: What does let result = 10 > 5 ? "yes" : "no"; assign to result?

Summary

🎉 Key Takeaways

  • if / else runs code based on whether a condition is true or false
  • else if chains let you check multiple conditions in order — first match wins
  • switch compares one value against many specific options — don't forget break!
  • Ternary (? :) is a compact if/else for simple value assignments
  • Guard clauses check for edge cases early to avoid deeply nested code
  • Truthy/falsy values work directly in conditions — no need for explicit comparisons

📚 Additional Resources

🚀 What's Next?

Now your programs can make decisions, but what about repeating actions? In the next lesson, you'll learn about loopsfor, while, and for...of — to process data, count, and repeat tasks efficiently.