if / else Statements
Conditionals are how a program makes decisions. The if statement runs a block of code only when a condition is met, and else if / else let you handle the alternatives. Mastering conditionals — and knowing when to flatten them — is one of the biggest factors in how readable your JavaScript turns out to be.
The basic if statement
An if statement evaluates an expression and executes its block when the result is truthy. The condition lives in parentheses; the body lives in braces.
const temperature = 32;
if (temperature > 30) {
console.log("It's hot outside — stay hydrated.");
}
Output:
It's hot outside — stay hydrated.
You can technically omit the braces for a single statement, but don’t. Always use braces — it prevents a whole class of bugs where a second line silently falls outside the conditional.
Tip: A common JavaScript footgun is writing
if (x = 5)(assignment) instead ofif (x === 5)(comparison). The single=assigns and then evaluates truthily, so the block always runs. Linters flag this — keep one enabled.
Adding else and else if
When you need a fallback, use else. When you need multiple mutually exclusive branches, chain else if. JavaScript evaluates each condition top to bottom and runs the first matching block, skipping the rest.
function gradeFor(score) {
if (score >= 90) {
return "A";
} else if (score >= 80) {
return "B";
} else if (score >= 70) {
return "C";
} else {
return "F";
}
}
console.log(gradeFor(95));
console.log(gradeFor(83));
console.log(gradeFor(40));
Output:
A
B
F
Because order matters, put the most specific or most likely conditions first. Once a branch matches, the rest are never checked.
Truthy and falsy conditions
The condition doesn’t have to be a boolean — JavaScript coerces it. Any value is either truthy or falsy. There are only a handful of falsy values; everything else is truthy.
| Falsy values | Notes |
|---|---|
false | the boolean itself |
0, -0, 0n | numeric and BigInt zero |
"" | empty string |
null | intentional absence |
undefined | uninitialized / missing |
NaN | ”not a number” |
const name = "";
if (name) {
console.log(`Hello, ${name}`);
} else {
console.log("Name is empty.");
}
Output:
Name is empty.
Gotcha: Empty arrays
[]and empty objects{}are truthy, not falsy. To check for an empty array, testarr.length === 0instead of relying on the value itself.
Combining conditions with logical operators
Use && (and), || (or), and ! (not) to build compound conditions. Both && and || short-circuit: evaluation stops as soon as the result is known, which is useful for guarding against errors.
const user = { name: "Ada", isAdmin: true };
if (user && user.isAdmin) {
console.log(`${user.name} has admin access.`);
}
The nullish coalescing operator ?? pairs nicely with conditionals when you only want to fall back on null or undefined (not on 0 or ""):
const config = { retries: 0 };
const retries = config.retries ?? 3; // 0, because 0 is not nullish
console.log(`Retries: ${retries}`);
Output:
Retries: 3
Wait — config.retries is 0, so ?? keeps it. The log above prints Retries: 0. Use ?? precisely when a valid 0 or "" must survive.
Nesting vs early returns
Deeply nested if statements (the “arrow anti-pattern”) are hard to read. Compare a nested version with a flattened one using guard clauses — early returns that handle invalid cases up front and let the happy path stay un-indented.
// Nested — harder to follow
function checkout(cart) {
if (cart) {
if (cart.items.length > 0) {
if (cart.paymentMethod) {
return "Order placed!";
}
}
}
return "Cannot check out.";
}
Rewritten with guard clauses:
function checkout(cart) {
if (!cart) return "Cannot check out: no cart.";
if (cart.items.length === 0) return "Cannot check out: cart is empty.";
if (!cart.paymentMethod) return "Cannot check out: no payment method.";
return "Order placed!";
}
console.log(checkout({ items: [], paymentMethod: "card" }));
console.log(checkout({ items: ["book"], paymentMethod: "card" }));
Output:
Cannot check out: cart is empty.
Order placed!
Each guard clause states one requirement and exits immediately, so the main logic reads like a straight line.
Ternary for simple value selection
When an if/else only picks between two values, the conditional (ternary) operator is more concise. Reserve it for expressions — not for branching side effects.
const age = 20;
const ticket = age >= 18 ? "Adult" : "Child";
console.log(ticket);
Output:
Adult
Avoid nesting ternaries more than one level deep; reach for if/else if or a switch once you have three or more branches.
Best practices
- Always use braces, even for one-line bodies — it prevents accidental scope bugs.
- Use strict equality (
===/!==) so you never get surprised by type coercion. - Prefer guard clauses and early returns over deep nesting to keep the happy path flat.
- Order
else ifchains from most specific to least specific. - Use
??(not||) when0,"", orfalseare legitimate values to keep. - Reach for a ternary only when selecting a value; use a full
ifwhen running statements. - Once you have many discrete cases on a single variable, consider
switchfor clarity.