JavaScript If Else
The if...else statement is how JavaScript programs make decisions. It lets your code run different blocks depending on whether a condition is true or false, which is the foundation of almost every non-trivial program. Without conditional logic, a script could only ever execute the same sequence of statements every single time, regardless of input. Mastering if...else is one of the very first real milestones in learning to program, and understanding its quirks well will save you from a huge number of bugs later on.
Overview / How It Works
An if statement tells JavaScript: “evaluate this condition, and if it is truthy, run this block of code.” You can extend it with an optional else block that runs when the condition is falsy, and with one or more else if blocks to test additional conditions in sequence. JavaScript evaluates these top to bottom and stops at the very first branch whose condition is true — it never checks the remaining conditions once a match is found, and if none of them match, the final else block (if present) runs as the fallback.
A critical detail is that the condition inside the parentheses does not have to be a strict true or false value. JavaScript coerces whatever value you give it into a boolean using its internal ToBoolean abstract operation. Every value in JavaScript is either “truthy” or “falsy” when evaluated in a boolean context. There are exactly six falsy values in the language: false, 0 (and -0), "" (empty string), null, undefined, and NaN. Every other value — including "0", "false" as strings, empty arrays [], and empty objects {} — is truthy. This trips up a lot of beginners because an empty array looks “empty” but is still truthy.
Under the hood, the JavaScript engine parses an if...else chain into a single conditional branch in its internal bytecode/execution plan. Each block you write, even a single statement without braces, creates its own lexical scope for let and const declarations (this is why a variable declared inside an if block with let is not visible outside of it). The engine evaluates the condition expression, converts the result with ToBoolean, and jumps to whichever block corresponds to the outcome, skipping every other branch entirely — conditions after a match are never evaluated, which matters if a later condition has side effects like a function call.
Syntax
if (condition1) {
// runs when condition1 is truthy
} else if (condition2) {
// runs when condition1 is falsy and condition2 is truthy
} else {
// runs when none of the above conditions are truthy
}
- condition1, condition2 — any expression; it is coerced to a boolean via
ToBoolean. - { } — a block statement; every path can contain multiple statements. Braces are optional for a single statement but strongly recommended (see Common Mistakes).
- else if — optional, and you can chain as many as you need; each is checked only if every prior condition was falsy.
- else — optional; always the last branch and never has its own condition.
The Conditional (Ternary) Operator
For simple two-way branches that just assign a value, JavaScript offers a shorthand: the ternary operator condition ? valueIfTrue : valueIfFalse. It is an expression (it produces a value), whereas if...else is a statement (it does not). Use a ternary only when the logic is simple — nesting multiple ternaries hurts readability fast.
Examples
Example 1: A Basic If/Else
const age = 20;
if (age >= 18) {
console.log("You are an adult.");
} else {
console.log("You are a minor.");
}
Output:
You are an adult.
Here age >= 18 evaluates to the boolean true, so the first block runs and the else block is skipped entirely.
Example 2: Chaining With Else If
const score = 82;
let grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else {
grade = "F";
}
console.log(`Score: ${score} -> Grade: ${grade}`);
Output:
Score: 82 -> Grade: B
JavaScript checks score >= 90 first (false), then score >= 80 (true), assigns "B", and stops — it never even evaluates score >= 70. Order matters: if the branches were written smallest-to-largest instead, every score of 70 or higher would incorrectly match the first branch.
Example 3: Nested Conditionals and Truthiness
function checkLogin(username, password) {
if (username) {
if (password && password.length >= 8) {
console.log("Login successful.");
} else {
console.log("Password must be at least 8 characters.");
}
} else {
console.log("Username is required.");
}
}
checkLogin("alice", "short");
checkLogin("", "somepassword");
checkLogin("bob", "longenoughpassword");
Output:
Password must be at least 8 characters.
Username is required.
Login successful.
This example nests one if...else inside another and relies on truthy/falsy coercion twice: an empty string "" is falsy, so checkLogin("", ...) falls into the outer else without ever checking the password. The && operator short-circuits too — if password is falsy (like undefined), password.length is never accessed, which avoids a TypeError.
Under the Hood: Step by Step
- The engine evaluates the first condition expression and converts the result using
ToBoolean(objects, non-empty strings, and non-zero numbers all becometrue). - If the result is
true, it executes the associated block and then jumps straight past every remainingelse if/elsebranch — it does not evaluate them at all. - If the result is
false, it moves to the next condition (anelse if, if present) and repeats the same evaluate-and-branch process. - If every condition is falsy and an
elseblock exists, that block runs unconditionally; if there is noelse, the engine simply continues on to whatever statement follows the whole chain. - Each
{ }block creates its own block scope, so aletorconstdeclared inside one branch does not leak into sibling branches or the surrounding code.
Common Mistakes
Mistake 1: Using = Instead of ===
A single = inside a condition performs assignment, not comparison, and the expression evaluates to the assigned value itself (which then gets coerced to a boolean). This is valid JavaScript syntax, so it will not throw an error — it will just silently do the wrong thing.
let x = 0;
if (x = 5) {
console.log("This runs because x = 5 is an assignment, and 5 is truthy.");
}
console.log(x);
Output:
This runs because x = 5 is an assignment, and 5 is truthy.
5
The fix is to always use === (or == when intentionally allowing type coercion) for comparisons:
let x = 0;
if (x === 5) {
console.log("This will not run since x is 0.");
} else {
console.log("x is not equal to 5.");
}
Output:
x is not equal to 5.
Mistake 2: Omitting Braces on Multi-Line Blocks
Without { }, only the single statement immediately after the condition belongs to the if. Indentation is purely cosmetic to JavaScript and does not define the block.
function logStatus(isActive) {
if (isActive)
console.log("Active");
console.log("Checked status");
}
logStatus(false);
Output:
Checked status
Even though console.log("Checked status") looks like it is inside the if, it actually runs unconditionally every time because only the very next statement was attached to the condition. Always wrap blocks in braces, even single-line ones, to avoid this exact bug when the code is later edited.
Best Practices
- Always use
{ }aroundif/elsebodies, even for one-line statements, so future edits cannot silently detach code from the condition. - Use strict equality (
===/!==) instead of loose equality (==/!=) unless you specifically need type coercion. - Order
else ifchains from most specific to least specific (e.g. highest threshold first) when ranges can overlap. - Prefer early returns in functions over deep nesting — return early on invalid input instead of wrapping the rest of the function body in an
else. - Use the ternary operator only for short, single-value expressions; reach for full
if...elseonce the logic involves multiple statements or side effects. - Be explicit with truthy/falsy checks — write
if (value !== null)orif (value !== undefined)instead of a bareif (value)when zero, an empty string, orNaNare valid values you don’t want to accidentally treat as “missing.” - Avoid nesting more than two or three levels deep; extract nested conditionals into well-named helper functions to keep logic readable.
Practice Exercises
- Exercise 1: Write a function
describeNumber(n)that logs"positive","negative", or"zero"depending on the sign ofn, using anif...else if...elsechain. - Exercise 2: Write a function
canVote(age, isCitizen)that returnstrueonly ifageis 18 or older ANDisCitizenistrue, otherwise returnsfalse. Try solving it both with nestedifstatements and with a single combined condition. - Exercise 3: Given a variable
temperature, write conditional logic that logs"freezing"for 32°F or below,"cold"for 33–60°F,"mild"for 61–80°F, and"hot"above 80°F. Test it with at least four different temperature values and predict the output before running it.
Summary
ifruns a block only when its condition is truthy;elseprovides a fallback for when it is falsy.else iflets you chain multiple conditions, and only the first truthy branch executes — the rest are skipped.- Conditions are coerced to boolean via
ToBoolean; onlyfalse,0,"",null,undefined, andNaNare falsy. - Always use
===instead of=in conditions, and always wrap blocks in{ }to avoid subtle bugs. - The ternary operator (
condition ? a : b) is a concise expression form ofif...else, best reserved for simple value assignments. - Each block creates its own scope for
let/const, and order matters when ranges in anelse ifchain can overlap.
