⚡ Phase 1 · Foundations 🟡 Intermediate MODULE 05

Conditional Statements

⏱️ 30 min read
📖 Theory + Code
🧩 5 Quiz Questions
🏗️ 1 Challenge
Your progress in Phase 135%
🎯 What you'll learn: How to make decisions with if/else, switch statements, ternary operator, truthy/falsy values, short-circuit evaluation, guard clauses, and build real-world condition-based programs.

if / else if / else

Conditionals let your code make decisions based on whether something is true or false.

if-else.js
JS
const age = 20;

if (age >= 18) {
  console.log("You are an adult.");
} else if (age >= 13) {
  console.log("You are a teenager.");
} else {
  console.log("You are a child.");
}
// Output: "You are an adult."

// Multiple conditions
const hour = 14; // 2 PM
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!");
}
Only the first TRUE branch executes
else if lets you check multiple conditions. Only the first TRUE branch executes — the rest are skipped entirely, even if they would also be true.

switch Statement

switch is cleaner when you're comparing ONE variable against MANY specific values.

switch.js
JS
const day = "Monday";

switch(day) {
  case "Monday":
  case "Tuesday":
  case "Wednesday":
  case "Thursday":
  case "Friday":
    console.log("Weekday — time to code!");
    break;
  case "Saturday":
  case "Sunday":
    console.log("Weekend — still code but chill!");
    break;
  default:
    console.log("Invalid day");
}

// Another example: HTTP status codes
const statusCode = 404;
switch(statusCode) {
  case 200: console.log("OK"); break;
  case 301: console.log("Moved Permanently"); break;
  case 404: console.log("Not Found"); break;
  case 500: console.log("Server Error"); break;
  default: console.log("Unknown status");
}
⚠️
Always use break; to prevent fall-through
ALWAYS use break; at the end of each case (unless you intentionally want "fall-through"). Forgetting break is one of the most common switch bugs!

Truthy and Falsy Values

In JavaScript, every value is either "truthy" (behaves like true) or "falsy" (behaves like false) when used in a condition.

The 7 (actually 8) Falsy Values

falsy-values.js
JS
// The falsy values in JavaScript:
if (false)       console.log("falsy"); // 1. false
if (0)           console.log("falsy"); // 2. 0
if (-0)          console.log("falsy"); // 3. -0
if (0n)          console.log("falsy"); // 4. BigInt 0
if ("")          console.log("falsy"); // 5. empty string
if (null)        console.log("falsy"); // 6. null
if (undefined)   console.log("falsy"); // 7. undefined
if (NaN)         console.log("falsy"); // 8. NaN

// EVERYTHING ELSE is truthy:
if (1)           console.log("truthy"); // any non-zero number
if ("0")         console.log("truthy"); // non-empty string (even "0"!)
if ([])          console.log("truthy"); // empty array is TRUTHY!
if ({})          console.log("truthy"); // empty object is TRUTHY!
if ("false")     console.log("truthy"); // non-empty string
⚠️
Common trap: [] and {} are truthy!
An empty array [] and empty object {} are TRUTHY in JavaScript. Only "" (empty string) is falsy — not empty array or object. This surprises many developers!

Practical Truthy/Falsy Usage

truthy-practical.js
JS
// Check if string is not empty
const username = "Alice";
if (username) {
  console.log(`Hello, ${username}!`);
} else {
  console.log("Please enter a username");
}

// Check if array has items
const items = [1, 2, 3];
if (items.length) {
  console.log(`You have ${items.length} items`);
}

Short-Circuit Evaluation

JavaScript's && and || operators don't just return true or false — they return the actual value at which evaluation stops.

short-circuit.js
JS
// && stops at first falsy
const user = null;
const name = user && user.name; // null (short-circuits at user)
console.log(name); // null

// || stops at first truthy
const displayName = "" || "Anonymous";
console.log(displayName); // "Anonymous"

// Practical: conditional function call
const logError = (err) => console.error(err);
const error = "Something went wrong";
error && logError(error); // only calls if error is truthy

// Guard pattern
function greet(name) {
  name || (name = "Stranger"); // same as: if (!name) name = "Stranger"
  return `Hello, ${name}!`;
}
console.log(greet(""));      // "Hello, Stranger!"
console.log(greet("Alice")); // "Hello, Alice!"

Guard Clauses Pattern

Guard clauses return early from a function when conditions aren't met — this keeps code flat and readable.

guard-clauses.js
JS
// WITHOUT guard clauses (nested, hard to read)
function processOrder(order) {
  if (order) {
    if (order.items) {
      if (order.items.length > 0) {
        if (order.user) {
          console.log("Processing order...");
        }
      }
    }
  }
}

// WITH guard clauses (flat, clean, readable)
function processOrder2(order) {
  if (!order) return console.log("No order");
  if (!order.items) return console.log("No items");
  if (order.items.length === 0) return console.log("Empty cart");
  if (!order.user) return console.log("Not logged in");

  console.log("Processing order..."); // only reached if ALL checks pass
}
Guard clauses are a professional pattern
Guard clauses (early returns) reduce nesting, make code easier to read, and clarify exactly what conditions must be true. Senior developers use this pattern constantly.

Real-World Examples

Age Validator

age-validator.js
JS
function validateAge(age) {
  if (typeof age !== "number") return "Invalid: not a number";
  if (age < 0) return "Invalid: negative age";
  if (age > 120) return "Invalid: unrealistic age";
  if (age < 18) return "Minor — restricted access";
  if (age >= 65) return "Senior — apply discounts";
  return "Adult — full access granted";
}

console.log(validateAge(25));   // "Adult — full access granted"
console.log(validateAge(14));   // "Minor — restricted access"
console.log(validateAge(-5));   // "Invalid: negative age"

Grade System

grade-system.js
JS
function getGrade(score) {
  if (score < 0 || score > 100) return "Invalid score";
  if (score >= 90) return "A — Excellent!";
  if (score >= 80) return "B — Good";
  if (score >= 70) return "C — Average";
  if (score >= 60) return "D — Below Average";
  return "F — Fail";
}

console.log(getGrade(95));  // "A — Excellent!"
console.log(getGrade(72));  // "C — Average"
console.log(getGrade(55));  // "F — Fail"

Login Checker

login-checker.js
JS
const credentials = {
  username: "admin",
  password: "secret123",
  isActive: true
};

function login(username, password) {
  // Guard clauses
  if (!username || !password) return "Missing credentials";
  if (username !== credentials.username) return "User not found";
  if (password !== credentials.password) return "Wrong password";
  if (!credentials.isActive) return "Account deactivated";

  return `Welcome back, ${username}!`;
}

console.log(login("admin", "secret123")); // "Welcome back, admin!"
console.log(login("admin", "wrong"));     // "Wrong password"
console.log(login("", "pass"));           // "Missing credentials"

Nested Conditions — and When to Avoid

nested-refactor.js
JS
// Deeply nested — hard to read (code smell)
function checkAccess(user, resource) {
  if (user) {
    if (user.isLoggedIn) {
      if (user.role === "admin") {
        if (resource.exists) {
          return true;
        }
      }
    }
  }
  return false;
}

// Refactored with guard clauses + combined && conditions
function checkAccess2(user, resource) {
  if (!user || !user.isLoggedIn) return false;
  if (user.role !== "admin") return false;
  if (!resource.exists) return false;
  return true;
}
Deeply nested if = code smell
Deeply nested if statements are a code smell. Refactor with guard clauses, combine conditions with &&/||, or use switch. Your future self (and teammates) will thank you.

Lesson Summary

if / else if / else — chain conditions, only the first TRUE branch runs.
switch — clean way to compare one value against many; always use break.
Truthy/Falsy — 8 falsy values; everything else is truthy. [] and {} are truthy!
Short-circuit&& stops at first falsy, || stops at first truthy.
Guard clauses — early returns flatten nesting and make code more readable.
Avoid deeply nested conditionals — combine with &&/|| or refactor to guards.
🧩 Knowledge Check — Lesson 5
Answer all 5 questions to test your understanding. Instant feedback on every answer.
1. Which of the following values is TRUTHY in JavaScript?
2. What happens if you forget break in a switch case?
3. What is a guard clause?
4. What does if (username) check when username is an empty string?
5. How do you check if BOTH conditions must be true?
💪
Coding Challenge — Lesson 5
Apply what you learned · Intermediate Level

Build a real BMI calculator that uses all the conditional patterns from this lesson.

Challenge: Build a BMI Calculator with Conditions

Create a file called bmiCalc.js. Write a function calculateBMI(weight, height) where weight is in kg and height is in meters.
  • Formula: BMI = weight / (height * height)
  • Under 18.5: "Underweight"
  • 18.5 to 24.9: "Normal weight"
  • 25 to 29.9: "Overweight"
  • 30 or above: "Obese"
  • Add guard clauses for: invalid inputs (negative/zero), missing values
  • Log: "Your BMI is X.XX — Category"
  • Test with at least 4 different inputs
💡 Show hints if you're stuck
  • Use toFixed(2) for rounding: bmi.toFixed(2)
  • Use guard clauses at the start: if (!weight || !height) return "Missing values";
  • Use an else if chain for categories after computing BMI
  • Remember: BMI is weight/(height²) — not weight/height
  • Test: 70kg, 1.75m → BMI ~22.9 → "Normal weight"
Finished this lesson?
Mark it complete to track your progress.
🎉

Lesson 5 Complete!

You can now make decisions in code — the foundation of all program logic.

Module 05 of 07 Phase 1 — JS Foundations