if / else
The if statement runs a block if its condition is truthy. The optional
else block runs when the condition is falsy.
const age = 20;
if (age >= 18) {
console.log("Adult");
} else {
console.log("Minor");
}
// "Adult"
// Falsy values that trigger else
if (0) { } // does not run (0 is falsy)
if ("") { } // does not run
if (null) { } // does not run
if (undefined) { } // does not run
if (NaN) { } // does not run
// Truthy values that trigger if
if (1) { } // runs (any nonzero number)
if ("0") { } // runs (non-empty string, even "false")
if ([]) { } // runs (empty array is truthy)
if ({}) { } // runs (empty object is truthy)
// Braces are optional for single statements - but always use them
if (age >= 18) console.log("ok"); // works but avoid this style
else if Chains
Chain multiple conditions with else if. Only the first matching block
executes; the rest are skipped.
function classify(score) {
if (score >= 90) {
return "A";
} else if (score >= 80) {
return "B";
} else if (score >= 70) {
return "C";
} else if (score >= 60) {
return "D";
} else {
return "F";
}
}
console.log(classify(95)); // "A"
console.log(classify(75)); // "C"
console.log(classify(55)); // "F"
// Alternative: lookup object (cleaner for fixed mappings)
function getDayName(num) {
const days = {
1: "Monday", 2: "Tuesday", 3: "Wednesday",
4: "Thursday",5: "Friday", 6: "Saturday", 7: "Sunday"
};
return days[num] ?? "Invalid day";
}
console.log(getDayName(3)); // "Wednesday"
switch / case
Use switch when comparing one value against multiple fixed options.
Each case needs a break to prevent falling through to the next.
const command = "start";
switch (command) {
case "start":
console.log("Starting...");
break;
case "stop":
console.log("Stopping...");
break;
case "pause":
console.log("Pausing...");
break;
default:
console.log(`Unknown command: ${command}`);
}
// "Starting..."
// Fallthrough: multiple cases share one block
const day = "Saturday";
switch (day) {
case "Saturday":
case "Sunday":
console.log("Weekend!");
break;
default:
console.log("Weekday");
}
// "Weekend!"
// Switch inside a function - use return instead of break
function getStatus(code) {
switch (code) {
case 200: return "OK";
case 404: return "Not Found";
case 500: return "Server Error";
default: return "Unknown";
}
}
console.log(getStatus(404)); // "Not Found"
Without break, JavaScript falls through to the next case and executes it too. This is rarely what you want and is a common source of bugs. Inside functions, prefer return over break - it eliminates the fall-through risk entirely and makes the intent clearer.
Ternary Operator
The ternary operator is a compact if/else for expressions. It returns a
value and is ideal for simple conditional assignments.
const age = 20;
// Ternary: condition ? valueIfTrue : valueIfFalse
const label = age >= 18 ? "Adult" : "Minor";
console.log(label); // "Adult"
// In template literals
const name = "Alice";
console.log(`Welcome, ${name ? name : "Guest"}!`);
// Nested ternary - keep shallow for readability
const score = 75;
const grade = score >= 90 ? "A"
: score >= 70 ? "B"
: score >= 50 ? "C"
: "F";
// Prefer if/else for complex conditions or multiple statements
// Ternary is for single-expression decisions only
Short-Circuit Evaluation
Logical operators && and || stop evaluating as soon as the
result is determined. This enables compact conditional logic.
// && - evaluates right side only if left is truthy
const user = { name: "Alice", isAdmin: true };
user.isAdmin && console.log("Show admin panel"); // "Show admin panel"
const guest = null;
guest && console.log(guest.name); // safe - right side never runs
// || - evaluates right side only if left is falsy
const port = process.env.PORT || 3000; // default value pattern
const displayName = user.name || "Anonymous";
// ?? - right side only for null/undefined (not 0 or "")
const timeout = config.timeout ?? 5000;
const retries = config.retries ?? 3;
// Combining for conditional rendering (React-style pattern)
const isLoggedIn = true;
const element = isLoggedIn && `Welcome, ${user.name}`;
// Warning: 0 && ... evaluates to 0 (renders "0" in React)
const count = 0;
const show = count && "Items"; // 0 - not false, renders as 0!
const showFix = count > 0 && "Items"; // false - correct
Guard Clauses (Early Return)
Guard clauses return early from a function when conditions are not met. They flatten
nested if/else into a linear, readable structure.
// WITHOUT guard clauses - deeply nested
function processOrder(user, cart, payment) {
if (user) {
if (cart.items.length > 0) {
if (payment.isValid) {
// actual logic buried here
return placeOrder(user, cart, payment);
} else {
return { error: "Invalid payment" };
}
} else {
return { error: "Cart is empty" };
}
} else {
return { error: "Not logged in" };
}
}
// WITH guard clauses - flat and easy to read
function processOrderClean(user, cart, payment) {
if (!user) return { error: "Not logged in" };
if (cart.items.length === 0) return { error: "Cart is empty" };
if (!payment.isValid) return { error: "Invalid payment" };
// happy path - no nesting
return placeOrder(user, cart, payment);
}
// Guard clauses for input validation
function divide(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("Arguments must be numbers");
}
if (b === 0) throw new Error("Division by zero");
return a / b;
}
Nullish and Optional Chaining in Conditions
const user = {
name: "Alice",
profile: null
};
// Checking nested properties safely
if (user?.profile?.bio) {
console.log(user.profile.bio);
} else {
console.log("No bio set");
}
// "No bio set"
// Checking with nullish coalescing for defaults
const theme = user?.settings?.theme ?? "light";
const lang = user?.settings?.language ?? "en";
// Combining conditions
function canEdit(user, doc) {
return user?.isAdmin || doc?.ownerId === user?.id;
}
// Checking array safely
const items = user?.cart?.items;
if (items?.length > 0) {
console.log(`Cart has ${items.length} items`);
}