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, andNaN— everything else is truthy. - Use guard clauses (early returns) to reduce nesting and improve readability.
- The nullish coalescing operator (
??) is safer than||for defaults when0or""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.