Conditionals
Table of Contents
Conditionals let your code make decisions. Instead of running every line top to bottom, you can branch — execute different code depending on whether something is true or false. This is fundamental to every program you’ll ever write.
if Statements
The simplest conditional: run code only if a condition is true.
const temperature = 35;
if (temperature > 30) {
console.log("It's hot outside!");
}
if…else
Handle both cases:
const age = 17;
if (age >= 18) {
console.log("You can vote.");
} else {
console.log("You're not old enough to vote yet.");
}
if…else if…else
Multiple conditions:
const score = 78;
if (score >= 90) {
console.log("A");
} else if (score >= 80) {
console.log("B");
} else if (score >= 70) {
console.log("C");
} else if (score >= 60) {
console.log("D");
} else {
console.log("F");
}
// "C"
Conditions are checked top to bottom. The first one that’s true wins — the rest are skipped.
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
=== |
Strict equality | 5 === 5 → true |
!== |
Strict inequality | 5 !== "5" → true |
> |
Greater than | 10 > 5 → true |
< |
Less than | 3 < 7 → true |
>= |
Greater than or equal | 5 >= 5 → true |
<= |
Less than or equal | 4 <= 3 → false |
=== and !== (strict equality) instead of == and !=. The loose operators do type coercion, which leads to surprising results: "0" == false is true, "" == 0 is true, null == undefined is true. Strict equality avoids these traps.
Logical Operators
Combine multiple conditions:
const age = 25;
const hasLicense = true;
const isSuspended = false;
// AND (&&) — both must be true
if (age >= 16 && hasLicense) {
console.log("You can drive.");
}
// OR (||) — at least one must be true
if (age < 16 || !hasLicense) {
console.log("You cannot drive.");
}
// NOT (!) — inverts a boolean
if (!isSuspended) {
console.log("License is active.");
}
// Combining them
if (age >= 16 && hasLicense && !isSuspended) {
console.log("You're good to go!");
}
Truthy and Falsy Values
In JavaScript, any value can be used as a condition. Values are either “truthy” (treated as true) or “falsy” (treated as false).
Falsy values (there are only 8):
false
0
-0
0n // BigInt zero
"" // empty string
null
undefined
NaN
Everything else is truthy, including:
"0" // non-empty string (even "0" and "false")
[] // empty array
{} // empty object
42 // any non-zero number
This matters because you’ll often see conditions like:
const username = getUserInput();
if (username) {
// username is not empty/null/undefined
console.log(`Welcome, ${username}`);
} else {
console.log("Please enter a username");
}
The Ternary Operator
A compact way to write simple if/else expressions:
// condition ? valueIfTrue : valueIfFalse
const age = 20;
const status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"
// Useful for inline decisions
const greeting = `Good ${new Date().getHours() < 12 ? "morning" : "afternoon"}!`;
// In template strings
const items = 3;
console.log(`You have ${items} item${items === 1 ? "" : "s"}`);
// "You have 3 items"
Don’t nest ternaries — they become unreadable. Use if/else instead:
// Bad — hard to read
const result = a > b ? "greater" : a < b ? "less" : "equal";
// Good — clear
let result;
if (a > b) {
result = "greater";
} else if (a < b) {
result = "less";
} else {
result = "equal";
}
switch Statements
When you’re comparing one value against many possible matches:
const day = new Date().getDay();
switch (day) {
case 0:
console.log("Sunday");
break;
case 1:
console.log("Monday");
break;
case 2:
console.log("Tuesday");
break;
case 3:
console.log("Wednesday");
break;
case 4:
console.log("Thursday");
break;
case 5:
console.log("Friday");
break;
case 6:
console.log("Saturday");
break;
}
break! Without it, execution “falls through” to the next case. This is occasionally useful on purpose, but usually it’s a bug.
Grouping cases
const day = "Saturday";
switch (day) {
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
console.log("Weekday");
break;
case "Saturday":
case "Sunday":
console.log("Weekend!");
break;
}
switch vs object lookup
For simple value mapping, an object is often cleaner than a switch:
// Instead of a long switch:
const statusCodes = {
200: "OK",
201: "Created",
400: "Bad Request",
404: "Not Found",
500: "Server Error"
};
const message = statusCodes[response.status] || "Unknown";
Nullish Coalescing (??)
Returns the right side only if the left side is null or undefined (not for other falsy values like 0 or ""):
const userAge = 0;
// || treats 0 as falsy — wrong!
const age1 = userAge || 25; // 25 (not what we want)
// ?? only triggers on null/undefined — correct!
const age2 = userAge ?? 25; // 0 (preserves the valid zero)
// Practical use: config with optional values
function createServer(options) {
const port = options.port ?? 3000;
const host = options.host ?? "localhost";
console.log(`Server at ${host}:${port}`);
}
createServer({ port: 0 }); // "Server at localhost:0" (port 0 is valid)
Optional Chaining (?.)
Safely access nested properties without checking each level:
const user = {
name: "Alice",
address: {
city: "Portland"
}
};
// Without optional chaining — verbose
const zip = user.address && user.address.zipCode;
// With optional chaining — clean
const zip2 = user.address?.zipCode; // undefined (no error)
const phone = user.contact?.phone; // undefined (no error)
const first = user.friends?.[0]; // undefined (no error)
const result = user.getName?.(); // undefined (no error)
Common Patterns
Guard clauses (early returns)
Instead of deeply nested if/else, return early for invalid cases:
// Nested (hard to read)
function processOrder(order) {
if (order) {
if (order.items.length > 0) {
if (order.payment) {
// actual logic buried deep
return submitOrder(order);
}
}
}
return null;
}
// Guard clauses (much cleaner)
function processOrder(order) {
if (!order) return null;
if (order.items.length === 0) return null;
if (!order.payment) return null;
return submitOrder(order);
}
Default values with destructuring
function createUser({ name, role = "viewer", active = true } = {}) {
return { name, role, active };
}
createUser({ name: "Alice" });
// { name: "Alice", role: "viewer", active: true }
Conditional object properties
const includeDebug = process.env.NODE_ENV === "development";
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
...(includeDebug && { debug: true, verbose: true })
};
Choosing the Right Conditional
| Situation | Best Choice |
|---|---|
| Simple true/false branch | if/else |
| Inline value selection | Ternary ? : |
| Multiple exact value matches | switch or object lookup |
| Default for null/undefined | ?? |
| Safe nested property access | ?. |
| Many conditions to check | if/else if with guard clauses |