JavaScript Conditions

Master control flow, boolean type coercion, switch fallthrough mechanics, expressions vs statements, and clean architectural guard clauses.

1. Control Flow & Boolean Coercion Mechanics

By default, JavaScript executes code sequentially from top to bottom. Conditional statements alter this linear execution by creating decision branches based on dynamic boolean expressions.

When an expression is passed into a condition like if (expression), the JavaScript engine performs an internal abstract operation called ToBoolean(argument) to coerce the value into either true or false.

The 8 Falsy Values in JavaScript ❌

In the entire JavaScript language, only these 8 values evaluate to false:

  • false (boolean literal)
  • 0, -0, 0n (BigInt zero)
  • "" (empty string with zero length)
  • null (intentional absence of value)
  • undefined (uninitialized variable)
  • NaN (Not-a-Number from invalid math)

Truthy Values (Everything Else! ✅)

All other values evaluate to true in boolean context, including:

  • "0" & "false" (any non-empty string is truthy)
  • [] (empty array — exists in memory!)
  • {} (empty object — exists in memory!)
  • function() {} (callable function object)
  • Any non-zero numbers (e.g., -10, 3.14)

💡 Why are [] and {} Truthy?

In JavaScript, arrays and objects are Reference Types stored in heap memory. Even if an array has 0 elements ([].length === 0), the variable holds a valid memory reference address. In boolean conversion, any valid object reference always evaluates to true! To verify an empty array, always check its length: if (arr.length > 0).

// Demonstrating Boolean Coercion:
console.log(Boolean([]));       // true (Valid heap reference)
console.log(Boolean({}));       // true (Valid heap reference)
console.log(Boolean(""));       // false (Empty primitive string)
console.log(Boolean("0"));      // true (Non-empty string)
console.log(Boolean(0));        // false (Numeric zero)
Output
true
true
false
true
false

2. The if, else if, and else Decision Ladder

The if-else ladder evaluates conditions sequentially from top to bottom. As soon as a branch condition evaluates to true, its code block executes, and the engine immediately skips all subsequent branches without evaluating them.

const studentScore = 85;

if (studentScore >= 90) {
    console.log("Grade: A+ (Honors)");
} else if (studentScore >= 80) {
    console.log("Grade: A (First Class)"); // Matches here -> Skips rest!
} else if (studentScore >= 70) {
    console.log("Grade: B");
} else {
    console.log("Grade: Needs Improvement");
}
Output
Grade: A (First Class)

3. The switch Statement & Fallthrough Mechanics

When you need to match a single variable against multiple discrete constant values (e.g., user roles, HTTP status codes, actions), a switch statement is often cleaner than a long chain of if-else checks.

Core Rules of switch:

  • Strict Comparison: switch compares expressions using strict equality (===). It will not perform automatic type conversions (e.g., "200" will not match 200).
  • Fallthrough Mechanics: Once a matching case is found, execution continues into subsequent cases sequentially until it encounters a break statement or the block ends.
const userRole = "admin";

switch (userRole) {
    case "admin":
        console.log("Permissions: Full Read, Write & Delete");
        break; // Prevents fallthrough into editor!
    case "editor":
        console.log("Permissions: Read & Write");
        break;
    case "viewer":
        console.log("Permissions: Read Only");
        break;
    default:
        console.log("Permissions: Access Denied");
}

// Intentional Fallthrough Grouping (Shared logic for multiple cases):
const currentDay = "Sunday";

switch (currentDay) {
    case "Saturday":
    case "Sunday":
        console.log("Schedule: Weekend Off! 🎉");
        break;
    default:
        console.log("Schedule: Workday 💻");
}
Output
Permissions: Full Read, Write & Delete
Schedule: Weekend Off! 🎉

4. The Ternary Operator: Expressions vs Statements

To understand the conditional operator (? :), you must understand the fundamental difference between an Expression and a Statement:

  • Statement (e.g., if-else): Performs an action or instruction. It produces no value of its own, so you cannot directly assign it to a variable (e.g., const x = if(a) { 1 } is a syntax error).
  • Expression (e.g., a ? b : c): Evaluates down to a concrete single value. It can be directly assigned to variables, passed into function parameters, or embedded in React JSX.

Clean: Single Binary Check ✅

Ideal for inline assignments and concise variable initialization.

const userAge = 20;
const permission = userAge >= 18 ? "Granted" : "Denied";
console.log(permission); // "Granted"

Anti-Pattern: Nested Ternaries ❌

Avoid chaining multiple ternaries. They hurt readability and maintainability.

const score = 85;
// Hard to read & debug:
const tag = score > 90 ? "A" : score > 80 ? "B" : "C";

5. Clean Architecture: The Guard Clause Pattern

In real-world software engineering, deeply nested if statements (often called the Pyramid of Doom or Arrow Anti-Pattern) dramatically increase Cyclomatic Complexity — making code harder to read, debug, and test.

The Solution: The Bouncer / Guard Clause Pattern:
Check for invalid conditions, missing data, and unauthorized access first, and return immediately. This keeps your core "happy path" logic flat, clean, and un-indented at the root level.

❌ Deeply Nested (Pyramid of Doom)

function placeOrder(user, cart) {
    if (user) {
        if (user.isLoggedIn) {
            if (cart.length > 0) {
                return "Order Placed!";
            } else {
                return "Cart is empty";
            }
        } else {
            return "Please login";
        }
    } else {
        return "No user found";
    }
}

✅ Clean Guard Clauses (Flat & Fail-Fast)

function placeOrder(user, cart) {
    // 1. Fail-fast error checks first
    if (!user) return "No user found";
    if (!user.isLoggedIn) return "Please login";
    if (cart.length === 0) return "Cart is empty";

    // 2. Main happy path sits clean & flat!
    return "Order Placed!";
}

6. Conditions Interview Deck

Test your understanding of JavaScript conditional branches, truthy/falsy coercion, and switch fallthrough:

🎯 Conditions Interview Deck
Question 1 of 5
⭐ Easy • Truthy / Falsy Empty Arrays
if ([]) {
    console.log("Array is truthy");
} else {
    console.log("Array is falsy");
}
What will be printed to the console?
A) "Array is truthy"
B) "Array is falsy"
C) TypeError
D) undefined
⚡ Medium • Switch Fallthrough Switch Statement
const val = 1;
switch (val) {
    case 1:
        console.log("One");
    case 2:
        console.log("Two");
        break;
    default:
        console.log("Default");
}
What will be printed to the console?
A) One
B) Two
C) One then Two
D) One then Default
⚡ Medium • String vs Number Coercion Boolean()
console.log(Boolean(0), Boolean("0"));
What will be printed to the console?
A) false, false
B) false, true
C) true, false
D) true, true
⭐ Easy • Ternary Operator Inline Conditions
const age = 20;
const status = age >= 18 ? "Adult" : "Minor";
console.log(status);
What will be printed to the console?
A) Minor
B) true
C) Adult
D) undefined
⭐ Easy • Guard Clauses Early Return
function verify(user) {
    if (user.age < 18) return "Invalid Age";
    return "Verified: " + user.name;
}
console.log(verify({ name: "Sam", age: 16 }));
What will be returned?
A) "Verified: Sam"
B) undefined
C) null
D) "Invalid Age"

7. Practice Challenge

🎯 Coding Challenge: Clean Guard Clauses

Write a function validateUser(user) using Guard Clauses (early returns):
1. If !user, return "Error: User required".
2. If !user.isVerified, return "Error: Unverified".
3. If user.age < 18, return "Error: Underage".
4. Otherwise, return "Access granted to " + user.name.

Your output will appear here.