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

Programs typically execute code sequentially. However, robust applications must make decisionsβ€”executing different logic paths based on application state (e.g., user authentication, input validation, feature flags).

Conditionals facilitate this control flow, running code blocks selectively based on boolean conditions. This is essential for creating interactive, responsive applications.

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 loops β€” for, while, and for...of β€” to process data, count, and repeat tasks efficiently.