Making decisions · Lesson 9 of 77 · concept
if, else, and blocks
About 50 minutes.
By the end of this lesson you can
- Write an if statement that runs some code only when a condition is true.
- Add else and else if so that exactly one branch of a decision runs.
- Use braces to group several statements into a single block.
- Explain why a single = inside a condition is almost always a bug.
So far, your programs have had no choices to make
An analogy
Everything you have written so far runs from the top of the file to the bottom, one line after another, and it does the same thing every time. That is fine for a program with exactly one job. It is useless for a program that has to react: a ticket price that depends on age, a warning that only appears when a number is too high, a message that changes when someone leaves a box empty.
Think of a recipe that says: bake for thirty minutes; if the top is not brown yet, bake for five more. The recipe is still read top to bottom. But one instruction is fenced off behind a question, and whether you carry it out depends on what you see when you get there. JavaScript spells that fence 'if'.
That is the whole idea of this module. You are not learning a new kind of value or a new kind of maths. You are learning how to put a fence around some lines of code and a question on the gate.
A condition is just an expression that comes out true or false
Explanation
The question on the gate is called a condition. It is not special syntax — it is an ordinary expression, of the kind you already write, that happens to produce a boolean. You met the comparison operators in the previous module: 'greater than' (>), 'less than' (<), 'greater than or equal' (>=), 'less than or equal' (<=), strict equality (===) and strict inequality (!==). Each one takes two values and produces true or false.
So 'age >= 18' is not a question hanging in the air. It is an expression with a value, the same way '2 + 2' is an expression with a value. If age is 21 then 'age >= 18' IS true, in exactly the sense that '2 + 2' IS 4.
One thing beginners expect and do not get: the condition is asked once, at the instant the program reaches that line, and then the program moves on. It is a snapshot, not a subscription. If the value changes a hundred lines later, nothing goes back and re-runs the branch. Nothing in this module watches anything.
The shape of an if statement
Explanation
An if statement is the keyword 'if', then a condition in round brackets, then the code to run in curly braces. The braces and everything between them are called a block, and the block runs either once or not at all — never twice, and never partly.
Read it out loud as a sentence and it stops looking like punctuation: 'if temperature is greater than thirty, then log this message'. The round brackets hold the question. The curly braces hold the answer.
Nothing else in the program changes. Lines above the if have already run. Lines below the if run afterwards either way. The if only controls what is inside its own braces.
else, and else if: exactly one path
Explanation
An if on its own has two outcomes: the block runs, or nothing happens. Often you want the second case to do something too. That is 'else': a block with no condition of its own, which runs precisely when the if's condition was false. The else clause is optional, and there is at most one of them.
For three or more outcomes you chain them: 'else if'. This is worth saying plainly, because it looks like a keyword and is not one. 'else if' is simply an if statement written as the body of the else clause. That is why it needs its own condition in brackets, and why there is no limit on how many you chain.
The rule that follows from that is the one to remember: the branches are tested in the order you wrote them, the FIRST one whose condition is true runs, and the rest are not even evaluated. If a score of 95 matches the first branch, the program never asks whether 95 is also above 70. It already left.
This makes the order of your branches part of the logic, not a matter of taste. Put the narrowest, most specific condition first and the widest last, or the wide one will swallow the cases meant for the narrow one. Lesson six of this module is about nothing else.
Braces turn many statements into one
A mental model
The useful way to think about a block is: curly braces bundle any number of statements into a single statement, so that anywhere JavaScript expects one thing to happen, you can hand it a whole group.
This matters because the braces after an if are optional. If you leave them out, the if governs exactly ONE statement — the next one — and no more. Indentation has no meaning in JavaScript. A line that is lined up neatly under an if but sits outside its braces is not inside the if, it just looks that way. The third worked example below shows exactly this bug.
The advice from that: always write the braces, even for a one-line body. It costs two characters and it removes an entire category of bug — the one where you come back tomorrow, add a second line to the body, and silently break the program.
One idea borrowed early: naming a decision so you can reuse it
Explanation
The exercises in this module ask you to make a decision about an input that changes — an age, a month name, a weight — and you cannot try many inputs if the value is fixed at the top of the file. So we borrow one idea from the next module, just enough to use it.
A function is a named recipe with a blank in it. You write 'function ticketPrice(age) {' then the body, then a closing brace. 'age' is the blank, called a parameter: a name for whatever value gets handed in. Inside the body you use it like any other value. When you reach 'return something;' the function stops immediately and hands that value back to whoever called it.
Calling it means writing the name with a value in brackets: 'ticketPrice(15)'. That runs the body with age set to 15 and produces whatever was returned, which you can log or store. Nothing about a function is running until it is called.
That is genuinely all you need here, and it is why 'return' shows up inside if branches throughout this module: returning from a branch both answers the question and ends the function, which is often clearer than assigning to a variable. Module 3 covers functions properly — defaults, arrows, scope, closures. Take this as a loan, not as the full story.
Check there: A function is defined with the function keyword, a name, a parameter list in parentheses and a body in braces; return ends the call and supplies its value.
One equals sign assigns. Three compare.
A common mistake
Writing 'if (ready = true)' when you meant 'if (ready === true)' is the classic first-week bug, and the cruel part is that it is not an error. It runs. A single = is assignment: it puts true into ready, throwing away whatever was there, and then the whole assignment expression produces the value that was just assigned. That value is true, so the block runs — and it will run every single time, no matter what ready was.
The same shape with a number is just as quiet: 'if (count = 0)' sets count to zero and produces 0, which is false-ish, so the block never runs and your variable has been wiped as a bonus.
The fix is mechanical. Comparisons use ===. If you ever find yourself typing = inside the round brackets of an if, stop and look again. And if a branch runs every time or never runs, this is the first thing to check.
Recap
Recap
A condition is an ordinary expression producing true or false, evaluated once when the program reaches it. 'if' runs a block only when its condition is true. 'else' covers the false case. 'else if' is an if inside the else clause, so branches are tested top to bottom and the first match wins. Braces bundle statements into one block, and without them an if governs only the next single statement. A single = in a condition assigns instead of comparing.
Worked examples
Every output below was produced by actually running the code in the same sandbox your exercises run in — it is not a prediction.
A first decision
The smallest complete if/else. Read the condition as a question asked once, and notice that exactly one of the two blocks produces a line of output.
1const temperature = 31;2 3if (temperature > 30) {4 console.log("Too hot - stay inside.");5} else {6 console.log("Fine - go outside.");7}- line 3
The condition is evaluated right here and produces true, because 31 > 30. Nothing is remembered or re-checked later.
- line 4
This line is inside the first block, so it runs only in the true case.
- line 5
else has no condition of its own. It is the 'everything the if missed' branch, and it is optional.
What it prints
Too hot - stay inside.A ladder of else if, where order is the logic
Four possible answers from one input. Watch what happens for 70: several conditions below it would also be true, and none of them are ever tested, because the first match ends the ladder.
1function band(score) {2 if (score >= 90) {3 return "excellent";4 } else if (score >= 70) {5 return "good";6 } else if (score >= 50) {7 return "pass";8 } else {9 return "fail";10 }11}12 13console.log(band(95));14console.log(band(70));15console.log(band(49));- line 2
Tested first. For 95 this is true, the function returns, and the rest of the ladder never runs.
- line 4
Reached only when the branch above was false. So 'score is at least 70' really means 'at least 70 and below 90' by the time you are standing here.
- line 8
The catch-all. Every input that reaches this point has already failed all three conditions, so no input is left without an answer.
- line 13
70 is also >= 50, but that branch is unreachable for this input: 'good' was found first.
What it prints
excellentgoodfail
The bug you get for free by skipping the braces
Both halves of this example are legal JavaScript and both are indented to look correct. Only one of them does what the indentation suggests. This is why the rule is: always write the braces.
1let total = 0;2const isMember = false;3 4if (isMember) total = total + 10;5console.log("Points:", total);6 7if (isMember)8 total = total + 10;9 console.log("This line is not part of the if.");- line 4
Legal and honest: one statement on the same line, clearly governed by the if. isMember is false, so total stays 0.
- line 8
This single statement is the entire body of the second if. It does not run.
- line 9
Indented to match, but the body already ended at the previous line. This runs unconditionally - the indentation is a lie the language never reads.
What it prints
Points: 0This line is not part of the if.
Check yourself
Not scored, not stored. Getting one wrong is the useful part.
In the worked example ladder, band(95) is called. Both 'score >= 90' and 'score >= 70' would be true for 95. Which branches run?
stock is 0. Work out which single branch runs, then check the output. Remember that the branches are tried in order.
const stock = 0;if (stock > 0) { console.log("In stock");} else if (stock === 0) { console.log("Sold out");} else { console.log("Unknown");}
What does 'if (ready = true) { ... }' do, if ready currently holds false?
Write it yourself
Exercise · intro · 6 tests
Positive, negative, or zero
Write a function describeSign(value) that takes a number and returns one of three pieces of text: 'positive' when the number is above zero, 'negative' when it is below zero, and 'zero' when it is exactly zero. Use an if / else if / else chain so that every number gets exactly one answer, and think about which condition has to come last. The starter code always returns 'positive', so it is right for a third of the inputs and wrong for the rest.
describeSign returns the string 'positive' for any value greater than 0, 'negative' for any value less than 0, and 'zero' for 0. Fractional numbers count too: 0.5 is positive and -0.5 is negative. The function always returns a string and never prints anything.
Define describeSign at the top level of your code — the tests call it by name.
Exercise · core · 8 tests
Ticket prices with boundaries that bite
A cinema charges by age. Under 5: free (0). 5 to 17 inclusive: 8. 18 to 64 inclusive: 14. 65 and over: 9. Write ticketPrice(age) returning the right number. Every boundary here is a place to be wrong by one, so decide deliberately whether each comparison is < or <=, and remember that in a chain the earlier branches have already ruled things out for you. The starter code handles two of the four bands.
ticketPrice returns a number: 0 for any age below 5, 8 for ages 5 to 17 inclusive, 14 for ages 18 to 64 inclusive, and 9 for ages 65 and above. The boundary ages 5, 18 and 65 belong to the higher band in each case.
Define ticketPrice at the top level of your code — the tests call it by name.
Worth remembering
- When is the condition of an if statement evaluated, and how often?
Once, at the moment the program reaches that line. It is a snapshot, not a subscription - if the value changes later, nothing re-runs the branch.
- In an if / else if / else chain, how many branches run?
Exactly one: the first whose condition is true. Later conditions are never tested. If none match and there is an else, the else runs.
- What does an if govern when you leave the braces out?
Exactly one statement - the next one. Indentation means nothing to JavaScript, so a second lined-up line runs unconditionally.
- What does a single = do inside the brackets of an if?
It assigns, then uses the assigned value as the condition - so the branch runs (or never runs) regardless of the old value. Comparison is ===.
Check this lesson against the source
We wrote the explanation; we did not invent the facts. This is the page that backs them.
Check there: The else clause is optional; the statement after if or else may be any statement including a block; and 'else if' is described as nesting an if statement inside the else clause.
Check there: A block groups zero or more statements and is delimited by a pair of braces.
Check there: The assignment operator returns the value that was assigned.
Prefer to read the source in a structured breakdown? Turn this doc into a breakdown