Conditions

Making your code choose between different paths based on a test.

What is it?

Most real programs need to make decisions: "if the user is logged in, show their name; otherwise, show a login button." Conditions are how you write that decision in code.

The main tool is the if statement — it runs a block of code only when a test is true, and can offer alternatives with else if and else.

Explain like I'm 10

It's like a fork in a road with a sign: 'If it's raining, take the covered path. Otherwise, take the shortcut.' Your code reads the sign and picks a path.

Examples

if / else if / else

const hour = 14;

if (hour < 12) {
  console.log("Good morning");
} else if (hour < 18) {
  console.log("Good afternoon");
} else {
  console.log("Good evening");
}
// logs: "Good afternoon"

A ternary expression for a simple either/or

const age = 20;

const message = age >= 18 ? "You can vote" : "You can't vote yet";
console.log(message); // "You can vote"

For a simple choice between two values, a ternary (condition ? ifTrue : ifFalse) is a compact one-line alternative to a full if/else.

How it works

JavaScript checks the condition inside the parentheses. If it evaluates to a truthy value — basically, anything except false, 0, "", null, undefined, or NaN (those are the "falsy" values) — it runs that block and skips the rest. Otherwise, it moves to the next else if and repeats the check, finally falling into else if nothing else matched.

Check condition
       ↓
 true?  ──yes──▶ run this block ──▶ done
   │
   no
   ↓
check next condition (repeat)

Why does it exist?

A program that always does the exact same thing regardless of input isn't very useful. Conditions let code adapt its behavior to the current data or situation.

When to use it

Use conditions whenever your program's next step depends on data you don't know in advance — a user's input, a value from an API, the current time. Anytime you can describe your logic with the word "if," you're describing a condition.

When not to use it

If every branch of an if/else ends up doing almost the same thing, a condition may be hiding a simpler solution — like a lookup object or a default value — that avoids repeating yourself. And a switch (or a lookup) usually reads better than five or more chained else if blocks checking the same variable.

Common mistakes

  • Forgetting the else and assuming a variable is always set inside the if block.

  • Using = instead of === inside a condition by accident.

  • Writing deeply nested if/else chains instead of simplifying the logic.

Practice exercises

  1. Easy:

    Write an if/else that logs "even" or "odd" for a given number.

  2. Medium:

    Write a grading function that returns a letter grade (A/B/C/D/F) from a numeric score using if/else if.

  3. Hard:

    Rewrite a chain of 4 else if checks using a switch statement instead.

Interview questions

What counts as a 'falsy' value in JavaScript, and how does that affect an `if` condition?

false, 0, -0, 0n, "" (empty string), null, undefined, and NaN are the only falsy values — every other value, including "0", [], and {}, is truthy. An if condition doesn't require a literal boolean; it converts whatever it's given to true/false using these rules before deciding which branch to run.

What is a ternary operator?

A compact, single-expression conditional: condition ? valueIfTrue : valueIfFalse. Unlike if/else, it's an expression that produces a value, so it can be used directly inside an assignment, a function argument, or a template string, not just as a standalone statement.

When would you reach for `switch` instead of `if/else`?

When you're comparing one single value against several specific, known possibilities — a status code, a day of the week, a menu option. It often reads more clearly than a long else if chain that keeps repeating the same variable, though for a small number of branches either works fine.

What's the difference between chaining `else if` and writing separate standalone `if` statements?

In an else if chain, only one branch ever runs — once a condition matches, the rest are skipped entirely. With separate if statements, every single one is checked independently and can run, even if an earlier one already matched, which usually isn't what's intended when the conditions overlap.

What happens if an `if` condition isn't a literal `true` or `false`, like `if (user)`?

JavaScript coerces whatever value the condition evaluates to into a boolean before deciding, using the truthy/falsy rules — if (user) runs its block whenever user is any truthy value (an object, a non-empty string, a non-zero number), and skips it for any falsy value, without ever needing an explicit === true.

Does `switch` compare its cases with `==` or `===`?

Strict equality (===) — a switch never coerces types when matching, so switch (1) { case "1": ... } will not match, even though 1 == "1" is true with loose equality.

What is 'fallthrough' in a `switch` statement, and why does `break` matter?

Without a break, execution doesn't stop after a matching case's code runs — it keeps falling into the code of the next case(s) below it, executing them too, until it hits a break or the end of the switch. break is what stops that and makes each case behave like an isolated branch.

Can you chain multiple `else if` blocks indefinitely? What decides which one actually runs?

Yes, there's no limit — JavaScript checks each condition top to bottom and runs the code for the very first one that evaluates truthy, then skips every remaining else if/else in that chain, even if a later condition would also have matched.

What's the practical difference between nesting `if` statements and chaining them with `else if`?

Nested ifs check conditions that depend on each other (an inner check only makes sense once an outer one already passed), while an else if chain checks a series of mutually exclusive alternatives for the same overall decision. Using deep nesting for what's really a set of alternatives makes code harder to read than a flat else if chain would.

Why might a lookup object be a better choice than a long `else if` chain?

When every branch just maps one specific input to one specific output (e.g. a status code to a message), a plain object used as a lookup table (messages[code]) replaces the whole chain with a single property access — it's shorter, avoids repeating the same variable name in every condition, and is easy to extend by just adding a key.

Why does `if (0)` skip its block, but `if ("0")` run it?

The number 0 is one of the fixed falsy values, so it coerces to false. The string "0" is a non-empty string, and every non-empty string is truthy regardless of its content — string truthiness depends only on length, not on what characters are inside.

Can a `switch` statement handle range checks or complex boolean conditions directly, like `age > 18`?

Not directly, since switch matches by strict equality against a fixed set of values. A common workaround is switch (true) { case age > 18: ... }, which works because each case expression is evaluated and compared against true — but at that point an if/else if chain is usually clearer.

Beyond syntax, what's a real difference between a ternary and an `if/else` statement?

A ternary is an expression — it always produces a value that can be used directly (assigned, passed as an argument, interpolated into a string). An if/else is a statement — it doesn't produce a usable value itself, so using it to choose a value requires first declaring a variable and assigning to it inside each branch.

Output-prediction: does `if ([]) console.log("truthy"); else console.log("falsy");` log "truthy" or "falsy", and why does that surprise people?

It logs "truthy". An empty array is an object, and every object — including [] and {} — is truthy regardless of whether it has any contents; only the specific fixed list of falsy values (false, 0, "", null, undefined, NaN) is falsy, and an empty array isn't on that list.

Output-prediction: what does `switch (1) { case "1": console.log("string one"); break; case 1: console.log("number one"); break; default: console.log("none"); }` log?

It logs "number one". switch compares with strict equality, so the number 1 doesn't match the case "1" (different types) and falls through to check case 1, which matches exactly.

Output-prediction: what does `let result; if (false) { result = "a"; } console.log(result);` log?

It logs undefined. result is declared but never assigned, since the if block's condition is false and its body never runs — there's no else to give it a value either, so it keeps its default undefined.

Trap: what's the bug in `switch (color) { case "red": console.log("stop"); case "green": console.log("go"); break; }`, and what does it print for `color = "red"`?

It prints both "stop" and "go". The "red" case is missing a break, so after running its own code, execution falls through into the next case's code unconditionally, regardless of whether color actually matched "green".

Output-prediction: what does `age >= 18 ? "adult" : age >= 13 ? "teen" : "child"` evaluate to when `age` is `15`?

"teen". Nested ternaries evaluate like chained else ifs: age >= 18 is false, so the expression falls to the part after the first :, which is itself another ternary — age >= 13 is true for 15, so that inner ternary evaluates to "teen".