Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~26 min· topic 3 of 7

Topic 2.3

Conditions: if, else and the Ternary

In one line

if runs a block of code only when a condition is truthy, else if and else add other paths, and the ternary condition ? a : b is the expression version that picks one of two values.

Think of it like this

Getting dressed in the morning. If it's raining, take an umbrella. Else if it's sunny, take sunglasses. Else, take nothing extra. You check the conditions in order and do only the first one that fits. A program decides the same way, thousands of times a second.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Control flow
The order in which the statements of a program run.
Condition
An expression checked for true or false, like age >= 18.
Block
A group of statements inside { } that runs together.
Branch
One of the possible paths through an if/else chain.
Statement vs expression
A statement does something (if, let); an expression produces a value (2 + 2, a ? b : c).
Ternary operator
condition ? a : b: gives a if the condition is truthy, otherwise b.
Guard clause
An early check that leaves a function straight away when something is wrong, keeping the rest of the code flat.

Step by step

01One condition, one block

The simplest form: if the condition is truthy, run the block. Otherwise skip it.

if.jswhole filejavascript
const temperature = 32;
if (temperature > 30) {
  console.log("It's hot. Drink water.");
}
console.log("This line always runs.");

02Add the other path with else

else catches everything the if didn't. Exactly one of the two blocks runs, never both, never neither.

else.jswhole filejavascript
const age = 15;
if (age >= 18) {
  console.log("You can vote.");
} else {
  console.log("Not yet.");
}

03Chain several conditions

else if checks the next condition only if all earlier ones were false. The first match wins, so order from most specific to least.

Chain several conditionsdiagram
Rendering diagram…

04Pick a value with the ternary

When both branches only produce a value, the ternary is shorter and can sit inside other expressions.

ternary.jswhole filejavascript
const items = 1;
console.log(`You have ${items} ${items === 1 ? "item" : "items"}`);

05Flatten with guard clauses

Handle the problem cases first and stop. What's left is the happy path, with no extra indentation. (You'll use return inside functions in Phase 3.)

guard.jswhole filejavascript
function checkout(cart) {
  if (!cart) return "No cart";
  if (cart.items.length === 0) return "Cart is empty";
  return "Paying for " + cart.items.length + " items";
}

Try it yourself

  1. 1

    Test the edges

    In the first example, predict the grade for 75, 74.9 and -5, then type each one and press the button. Edges (exactly 75, exactly 50) are where bugs hide.

  2. 2

    Reorder the branches

    Move the score >= 50 branch to the top of the chain. Predict: what does 95 get now? (C, because the first true branch wins.) Put it back.

  3. 3

    Open the shop

    In the second example, change hour to 10. Predict the text, the colour and the console line before running.

Code & diagrams

Grades with if / else if / else New tab

Type a score and press the button. The first matching branch wins, so `90` is an A and not also a B or C.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

A B C F
A B
The ternary is an expression New tab

The ternary sits inside a template literal and on the right of `=`, places where an `if` statement can't go. The nested one at the end works, but an `if` chain would be easier to read.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

closed medium
Truthy conditions and the 0 trap New tab

`if (stock)` can't tell "zero" from "missing". Say what you mean with a comparison.

Console

(nothing was printed)

Expected console output

A: treated as missing
B: stock is known: 0
C: please enter a name
D: sold out

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use = instead of === in a condition

In the third example, change const stock = 0; to let stock = 0;, then change if (stock !== undefined) to if (stock = 5).

terminal
$ Console
── what you'll see ──
A: treated as missing
B: stock is known: 5
C: please enter a name
D: 5 left

Break #2

Leave out the braces, then add a line

Write if (false) console.log("one"); console.log("two"); on two indented lines as if both belong to the if.

terminal
$ Console
── what you'll see ──
two

Myth vs fact

Myth

Several else if branches can run if several conditions are true.

Fact

Only the first truthy branch runs. Use separate if statements (no else) if you want every matching block to run.

Myth

The ternary is just a shorter if.

Fact

It's an expression that produces a value; if is a statement. You can't put return or a for loop inside a ternary branch, and you can't assign an if to a variable.

Myth

if (x) checks whether x exists.

Fact

It checks whether x is truthy. 0, "" and NaN exist but are falsy. Use x !== undefined, x != null (both null and undefined) or a comparison when those values matter.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Modern CPUs guess which way a branch will go (branch prediction). Predictable branches (almost always true or almost always false) are very cheap; random ones in hot loops cost more. JIT engines also optimise for the paths they have seen, so a branch that suddenly takes a new path may trigger deoptimisation. This only matters in very hot code.

  • ▸

    x != null (loose) is the one widely accepted use of ==: it's true for exactly null and undefined and false for everything else, so it's a compact "is present" check. Many style guides allow it as an exception to "always ===".

  • ▸

    A function declared inside an if block has different behaviour in strict mode (block-scoped) and sloppy mode (web-compat hoisting rules from the spec's Annex B). Avoid declaring functions inside blocks; assign a function expression to a const instead.

  • ▸

    When an if/else if chain only maps a value to another value, a lookup object or Map is often clearer and faster to extend (Topic 2.4 shows this).

Remember this

  1. 1

    Control flow is the order in which a program's statements run. Normally it's top to bottom. A conditional statement changes that: it runs some code only when a condition is met. if (condition) { ... } evaluates the condition, converts the result to a boolean with the truthy/falsy rules (Topic 1.6), and runs the block (the code between { and }) only if it is truthy. If not, it skips the block and carries on after it.

  2. 2

    else { ... } runs when the if condition was falsy. else if (other) { ... } adds another condition to check. A chain of if / else if / else is checked top to bottom, and only the first truthy branch runs; the rest are skipped, even if they would also be true. So order matters: put the most specific conditions first (check score >= 90 before score >= 50).

  3. 3

    Conditions are usually comparisons (age >= 18), equality checks (status === "paid") and logical combinations (isAdmin || isOwner, Topic 2.2). Because the condition is converted to a boolean, if (name) means "if name is not empty, 0, null or undefined". That's handy, but be careful: if (count) is false when count is 0, which is often a real value. Write if (count !== undefined) or if (count > 0) to say exactly what you mean.

  4. 4

    The ternary operator condition ? valueIfTrue : valueIfFalse is the only JavaScript operator with three operands. It's an expression: it produces a value, so you can put it in a variable, a template literal or a function argument: const label = age >= 18 ? "adult" : "child";. An if is a statement: it does something but has no value. Use the ternary to choose between two values, and if to choose between two actions. Nested ternaries (a ? b : c ? d : e) work but get hard to read fast.

  5. 5

    Guard clauses keep code flat. Instead of wrapping a whole function in if (valid) { ...lots of code... }, check the bad cases first and leave early: if (!user) return;. This early return style (you'll write functions in Phase 3) avoids deep nesting, sometimes called the "pyramid of doom".

  6. 6

    Braces: if (x) doThing(); without {} is allowed, but only the next single statement belongs to the if. Adding a second indented line later doesn't make it part of the if, which is a classic bug (Apple's 2014 "goto fail" security bug in C was exactly this). Most style guides require braces always. Also note that variables declared with let or const inside a block exist only inside that block (block scope, Topic 3.4).

Explain it without notes

01

How does an if / else if / else chain decide which block to run?

02

What is the difference between if and the ternary operator?

03

What is a guard clause, and why do people use it?

04

Why can if (count) be a bug?

Practice

01

Write code that prints fizz if n is divisible by 3, buzz if by 5, fizzbuzz if by both, and the number otherwise. Test with n = 15, 9, 10 and 7.

02

Use a ternary to print 1 item or N items for const n = 1 and const n = 4.

03

Rewrite this with a guard clause: if (age !== undefined) { if (age >= 18) { console.log("adult"); } else { console.log("minor"); } }.

Trade-offs

  • ↔

    Long if/else if chains are easy to write but hard to extend; a lookup table scales better when you're only mapping values.

  • ↔

    Nested ternaries are compact but slow to read; past one level, an if chain or a small function is kinder to the next reader.

Done when you can

  • I can write if, else if and else, and predict which branch runs.

  • I can order conditions so the most specific one is checked first.

  • I can use the ternary to choose a value inside an expression.

  • I can avoid the if (count) trap when 0 or "" are valid.

  • I always use braces and === in conditions.