intermediate Step 7 of 20

Control Flow

JavaScript Programming

Control Flow

Control flow structures determine the order in which statements are executed in your JavaScript programs. They allow your code to make decisions, choose between alternatives, and respond to different conditions. JavaScript provides if/else statements, switch/case blocks, the ternary operator, and the newer pattern matching features. Understanding truthy and falsy values in JavaScript is particularly important because the language's type coercion means that many non-boolean values can be used directly in conditions.

If/Else Statements

// Basic if
const age = 18;
if (age >= 18) {
    console.log("You are an adult.");
}

// If-else
const temperature = 35;
if (temperature > 30) {
    console.log("It's hot!");
} else {
    console.log("It's pleasant.");
}

// If-else if-else
const score = 85;
let grade;
if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else if (score >= 70) {
    grade = "C";
} else {
    grade = "F";
}
console.log(`Grade: ${grade}`);  // "Grade: B"

// Ternary operator (concise conditional)
const status = age >= 18 ? "adult" : "minor";
const message = score >= 60 ? "Pass" : "Fail";

// Nested ternary (avoid for readability)
const category = age < 13 ? "child" : age < 18 ? "teen" : "adult";

Truthy and Falsy Values

// Falsy values in JavaScript (only 8):
// false, 0, -0, 0n, "", null, undefined, NaN
// EVERYTHING else is truthy

// Common patterns using truthiness
const name = "";
if (name) {
    console.log(`Hello, ${name}`);
} else {
    console.log("Name is empty");  // This runs
}

// Guard clause pattern
function processUser(user) {
    if (!user) {
        console.log("No user provided");
        return;
    }
    if (!user.email) {
        console.log("User has no email");
        return;
    }
    // Main logic here — less nesting
    console.log(`Processing ${user.email}`);
}

// Short-circuit defaults
const config = {};
const port = config.port || 3000;          // 3000 (fallback for falsy)
const host = config.host ?? "localhost";    // "localhost" (fallback for null/undefined only)

// Logical AND for conditional execution
const isAdmin = true;
isAdmin && console.log("Welcome, admin!");  // Runs if isAdmin is truthy

Switch Statement

const day = new Date().getDay();
let dayName;

switch (day) {
    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";
}

// Fall-through (multiple cases sharing logic)
switch (day) {
    case 0:
    case 6:
        console.log("Weekend!");
        break;
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
        console.log("Weekday");
        break;
}

// Switch with strings
function getDiscount(memberLevel) {
    switch (memberLevel) {
        case "gold":    return 0.20;
        case "silver":  return 0.10;
        case "bronze":  return 0.05;
        default:        return 0;
    }
}

// Alternative: object lookup (often cleaner than switch)
const discounts = { gold: 0.20, silver: 0.10, bronze: 0.05 };
const discount = discounts[memberLevel] ?? 0;

Advanced Patterns

// Nullish coalescing for defaults
const userSettings = { theme: null, fontSize: 0 };
const theme = userSettings.theme ?? "light";       // "light" (null)
const fontSize = userSettings.fontSize ?? 16;      // 0 (not null/undefined)
const fontSizeBad = userSettings.fontSize || 16;   // 16 (0 is falsy!)

// Optional chaining with conditions
const user = { profile: { settings: { darkMode: true } } };
if (user?.profile?.settings?.darkMode) {
    console.log("Dark mode enabled");
}

// Using try/catch as control flow (for expected errors)
function parseJSON(str) {
    try {
        return { data: JSON.parse(str), error: null };
    } catch (e) {
        return { data: null, error: e.message };
    }
}

const { data, error } = parseJSON('{"valid": true}');
if (error) {
    console.log(`Parse error: ${error}`);
} else {
    console.log(data);
}
Pro tip: Use object lookups instead of long switch/if-else chains when mapping values. Instead of a 10-case switch, create an object with the mappings: const map = { a: 1, b: 2 }; return map[key] ?? defaultValue;. This is more concise, easier to maintain, and the mapping can be defined separately from the logic.

Key Takeaways

  • JavaScript has only 8 falsy values: false, 0, -0, 0n, "", null, undefined, and NaN — everything else is truthy.
  • Use guard clauses (early returns) to reduce nesting and improve readability.
  • The nullish coalescing operator (??) is safer than || for defaults when 0 or "" are valid values.
  • Object lookups are often cleaner than switch statements for value mapping.
  • Use the ternary operator for simple one-line conditions but avoid nesting them for readability.