Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
16 minLesson 5 of 35
JavaScript Fundamentals

Control Flow: if, switch, ternary

Control Flow: if, switch, ternary

Control flow determines which code runs and when. JavaScript provides if/else, switch, and the ternary operator for branching, plus some patterns that make your code more readable.

if / else if / else

const temperature = 22;

if (temperature > 30) {
    console.log("Hot day — stay hydrated");
} else if (temperature > 20) {
    console.log("Nice weather");
} else if (temperature > 10) {
    console.log("Chilly — bring a jacket");
} else {
    console.log("Cold!");
}

// Single-line form — OK for simple cases, skip the braces
if (isLoggedIn) redirect("/dashboard");

// But always use braces for multi-line blocks
// Avoids the dangling-else bug that caused Apple's SSL vulnerability in 2014

Truthy and Falsy Values

JavaScript converts values to booleans in conditions. Knowing what's falsy is essential:

// Falsy values — evaluate to false in boolean context
false
0
-0
0n          // BigInt zero
""          // empty string
null
undefined
NaN

// Everything else is truthy, including:
"0"         // non-empty string (even "false"!)
[]          // empty array
{}          // empty object
-1          // negative number
Infinity
// Common patterns using truthiness
const user = getUserFromDB();

if (user) {
    // user exists and is not null/undefined
    console.log(user.name);
}

if (items.length) {
    // array has at least one element
    renderList(items);
}

if (!isLoading && data) {
    // loading finished and data is available
    renderContent(data);
}

Early Return Pattern

One of the cleanest patterns in JavaScript — return early to avoid deeply nested code:

// Instead of this (nested, hard to read):
function processOrder(order) {
    if (order) {
        if (order.items.length > 0) {
            if (order.payment) {
                // actual logic buried here
                return completeOrder(order);
            } else {
                return "Payment required";
            }
        } else {
            return "Cart is empty";
        }
    } else {
        return "No order provided";
    }
}

// Use early returns (guard clauses):
function processOrder(order) {
    if (!order) return "No order provided";
    if (order.items.length === 0) return "Cart is empty";
    if (!order.payment) return "Payment required";
    
    // Happy path is unindented and obvious
    return completeOrder(order);
}

switch Statement

Use switch when comparing one value against multiple specific cases:

const day = new Date().getDay();  // 0=Sunday, 6=Saturday

switch (day) {
    case 0:
        console.log("Sunday");
        break;
    case 1:
        console.log("Monday");
        break;
    case 6:
        console.log("Saturday");
        break;
    default:
        console.log("Weekday");
}

// Fall-through (intentional): multiple cases, one action
switch (status) {
    case "pending":
    case "processing":
        showSpinner();
        break;
    case "complete":
        showSuccess();
        break;
    case "error":
    case "failed":
        showError();
        break;
    default:
        console.warn("Unknown status:", status);
}

switch uses strict equality (===) for comparison, so types must match exactly.

Ternary Operator

Perfect for simple conditional values — not for complex logic:

// condition ? valueIfTrue : valueIfFalse
const isAdult = age >= 18 ? "Yes" : "No";
const price = isMember ? 9.99 : 19.99;
const greeting = time < 12 ? "Good morning" : "Good afternoon";

// In template literals
console.log(`Status: ${isOnline ? "Online" : "Offline"}`);

// In JSX / HTML generation
const buttonHTML = `<button class="${isActive ? "btn-primary" : "btn-secondary"}">Click</button>`;

// Avoid nesting more than 2 levels
// This is OK:
const grade = score >= 90 ? "A" : score >= 70 ? "B" : "C";

// This is not:
const x = a ? b ? c : d : e ? f : g;  // please don't

Logical Operators as Control Flow

// && as a guard: "if left is truthy, evaluate right"
isLoggedIn && showDashboard();
user && user.profile && loadProfile(user.profile.id);

// || as a default: "if left is falsy, use right"
const name = user.name || "Anonymous";
const port = process.env.PORT || 3000;

// In React — conditional rendering
const element = isLoading && <Spinner />;

// ?? for null/undefined specifically
const timeout = config.timeout ?? 5000;

Short-Circuit Evaluation

// && stops at the first falsy value
function riskyOperation() {
    console.log("Running!");
    return true;
}

false && riskyOperation();  // "Running!" is NEVER logged
true && riskyOperation();   // "Running!" IS logged

// || stops at the first truthy value
"cached" || expensiveComputation();  // computation never runs

// This is why you see patterns like:
const cached = cache.get(key) || (cache.set(key, compute()), cache.get(key));

Nullish Coalescing vs OR

// The OR operator treats 0, "", false as "falsy"
const volume = userSetting || 50;
// Problem: if userSetting is 0 (mute), we get 50 instead!

// The ?? operator only triggers on null/undefined
const volume = userSetting ?? 50;
// Now: 0 stays 0, 50 is used only if setting is null/undefined

Optional Chaining in Conditions

const user = { profile: null };

// Without optional chaining — crashes if profile is null
if (user.profile.avatar) { }  // TypeError!

// With optional chaining — safely returns undefined
if (user?.profile?.avatar) {
    showAvatar(user.profile.avatar);
}

// With nullish coalescing
const avatar = user?.profile?.avatar ?? "/default-avatar.png";

Practical Example: Form Validation

function validateRegistration(formData) {
    const { username, email, password, confirmPassword } = formData;
    
    // Guard clauses for required fields
    if (!username?.trim()) return { error: "Username is required" };
    if (username.length < 3) return { error: "Username must be 3+ characters" };
    
    if (!email?.includes("@")) return { error: "Valid email required" };
    
    if (!password) return { error: "Password is required" };
    if (password.length < 8) return { error: "Password must be 8+ characters" };
    
    if (password !== confirmPassword) return { error: "Passwords don't match" };
    
    // All checks passed
    return { success: true, data: { username: username.trim(), email, password } };
}

Next lesson: Loops — for, while, forEach, and for...of for repeating operations efficiently.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!