if / else
The if statement is the most basic way to make a decision in JavaScript. You give it a condition, and it runs a block of code only when that condition is truthy. Pair it with else and else if and you have the core building block for almost every branching choice in a program — from "is the user logged in?" to "did this request fail?".
The basic form
The shape is always the same: the keyword, a parenthesised condition, and a block.
const age = 18;
if (age >= 18) {
console.log("adult");
}If the condition is truthy, the block runs. If it is falsy — false, 0, "", null, undefined, NaN — the block is skipped. You can omit the braces for a single statement, but most teams require them: missing braces are the source of a famously expensive bug class.
Adding else and else if
Use else for the "otherwise" branch, and chain else if for additional checks. JavaScript does not have a dedicated elif — else if is just an if nested inside an else.
function describe(score) {
if (score >= 90) {
return "excellent";
} else if (score >= 75) {
return "good";
} else if (score >= 50) {
return "passing";
} else {
return "failing";
}
}
console.log(describe(82)); // good
console.log(describe(42)); // failinggood failing
The conditions are checked top to bottom and the first one that matches wins. Order matters: if you swap the first two checks, every score above 75 becomes "good" and nothing ever reaches "excellent".
Truthy and falsy values
The condition does not have to be a boolean — JavaScript coerces it. Knowing the falsy set by heart pays off:
false0and-0(and0nfor BigInt)""(empty string)nullundefinedNaN
Everything else is truthy — including "0", "false", [] and {}. Test against the value you mean, not against == true.
const name = getName();
if (name) { // truthy: not empty, not null, not undefined
greet(name);
}
if (items.length) { // 0 is falsy — handles "no items" naturally
render(items);
}Guard clauses and early return
Deeply nested ifs are hard to read. The fix is the guard clause: bail out of the function as soon as a precondition fails, leaving the happy path unindented.
Nested — harder to follow
function charge(user, amount) {
if (user) {
if (user.active) {
if (amount > 0) {
// ...the real work, four levels deep
}
}
}
}Guard clauses — flat and clear
function charge(user, amount) {
if (!user) return;
if (!user.active) return;
if (amount <= 0) return;
// happy path lives at the top level
}Common patterns
A handful of idioms come up over and over:
// Default value when something is missing
let label;
if (user.nickname) {
label = user.nickname;
} else {
label = user.email;
}
// equivalent: const label = user.nickname || user.email;
// Conditional assignment to one of two values
const status = isOnline ? "green" : "grey";
// Range check — combine with && and ||
if (score >= 0 && score <= 100) {
// valid percentage
}if/else vs switch
When you are comparing the same value against many discrete possibilities, switch often reads better. When the branches involve ranges, multiple variables, or compound conditions, if/else wins.
Use switch — one value, many equal cases
switch (event.type) {
case "click": handleClick(); break;
case "keydown": handleKey(); break;
case "scroll": handleScroll(); break;
default: ignore();
}Use if/else — compound conditions
if (user.role === "admin" && feature.enabled) {
showAdminPanel();
} else if (user.role === "editor" && document.locked === false) {
showEditPanel();
} else {
showReadOnly();
}Common mistakes
Using
=instead of==/===—if (x = 5)assigns 5 toxand the condition is the assigned value. Most linters catch this.Relying on
==with mixed types. Prefer===so type coercion does not surprise you.Omitting braces and then adding a second line later — only the first line is part of the branch.
Long
else ifchains over the same value — that is the moment to reach forswitchor a lookup object.