Learn JavaScript

Lesson 1 of 9 ¡ if, switch and Loops

Module 4 ¡ if, switch and Loops

Decisions and Loops: The Picture

FreeReading

In this lesson

  • Write an if with an else that picks one of two lines, reading the condition as truthy or falsy.
  • Name the three parts of a for loop and say in which order they run.
  • Walk through every value of a list with for...of, and explain why a loop stops.

Zara's step counter keeps one number a day, and her goal is 8000 steps. She wants a line for each day of the week: met or short. Her first program checks day 1, then she copies the check for day 2, then for day 3. A week is seven copies, a month is thirty, and the copy for day 23 still says day 22. This lesson replaces every copy with one decision and one loop.

Zara's copied checks

Here is Zara's first program, for three days. Each check is the same five lines with a different day in it.

const day1 = 7500;
const day2 = 8000;
const day3 = 12030;
const goal = 8000;

if (day1 >= goal) {
  console.log("day 1: met");
} else {
  console.log("day 1: short");
}
if (day2 >= goal) {
  console.log("day 2: met");
} else {
  console.log("day 2: short");
}
if (day3 >= goal) {
  console.log("day 3: met");
} else {
  console.log("day 3: short");
}
day 1: short
day 2: met
day 3: met

It works, and it does not grow. Two ideas fix it. A decision runs some lines only when a condition holds: that is if. A loop runs the same lines again and again, once per day: that is for. Together they handle any number of days.

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

const GOAL = 8000;
const n = nextInt();
for (let day = 1; day <= n; day++) {
  const steps = nextInt();
  if (steps >= GOAL) {
    out.push("day " + day + ": met");
  } else {
    out.push("day " + day + ": short");
  }
}

console.log(out.join("\n"));
day 1: short
day 2: met
day 3: met
day 4: short
day 5: met
day 6: short
day 7: met

That output is for the input 7, then 7500 8000 12030 0 9100 4200 15000. The check is written once. The loop runs it seven times, and it would run it 365 times for a year without one extra line. So a loop repeats lines, and an if inside it decides what each repeat does.

if: lines that run only when a condition holds

An if statement has a condition in round brackets and a block in curly braces. A block is a group of lines that belong together. When the condition holds, the block runs. When it does not, the program skips the block and carries on after it.

const steps = 9100;
if (steps >= 8000) {
  console.log("Goal met. Well walked.");
}
console.log("Done for today.");
Goal met. Well walked.
Done for today.

Change steps to 500 and only the last line prints. An else block after the if runs in exactly the other case, so one of the two blocks always runs, never both.

The condition does not have to be a comparison. JavaScript reads any value as truthy or falsy, the rule from Module 2. The falsy values are false, 0 (with -0 and 0n), "", null, undefined and NaN, and everything else is truthy.

const name = "";
if (name) {
  console.log("Hello, " + name);
} else {
  console.log("Hello, stranger");
}

const unread = 3;
if (unread) {
  console.log(unread + " new messages");
}
Hello, stranger
3 new messages

The empty string is falsy, so the else block ran. The number 3 is truthy, so its block ran. So if (value) asks one question: is this value truthy? A comparison such as steps >= 8000 is just a value that is always true or false.

This track always writes the braces, even around one line. Common mistakes 2, near the end, shows what happens without them.

for: three parts, and the order they run in

The round brackets of a for loop hold three parts, separated by semicolons.

  • The start, let i = 1, runs once, before anything else.
  • The test, i <= 3, runs before every pass. When it is truthy, the body runs. When it is falsy, the loop ends.
  • The step, i++, runs after every pass of the body. It adds 1 to i.

The body is the block in braces, and one run of it is a pass. Watch the order. Step through the loop below and keep an eye on the test column: it is checked before each pass.

The start sets i to 1. The test runs before every pass and the step i++ runs after it. Three passes add 1, 2 and 3 to total. Then i is 4, the test 4 <= 3 is false, and the loop ends: the test ran four times for three passes.

Passitest i <= 3total after the body
11true, so the body runs1
22true, so the body runs3
33true, so the body runs6
none4false, so the loop ends6

Here is the same loop as a program. Each pass prints the value of i it saw.

let total = 0;
for (let i = 1; i <= 3; i++) {
  console.log("pass with i = " + i);
  total += i;
}
console.log("total " + total);
pass with i = 1
pass with i = 2
pass with i = 3
total 6

The test ran four times: three times it said yes, and the fourth time, with i at 4, it said no. The two pictures below put the if and the for side by side. The for is an if with an arrow back to the top.

The if flowchart and the for loop flowchart A decision runs one branch once; a loop runs its body until the test says no if and else the line before condition truthy? yes no the if block the else block the next line for (start; test; step) start: let i = 1 test: i <= 3 ? true body: total += i step: i++ again false after the loop The start runs once. The test runs before every pass, and the step after every body.

So in a for loop, the start runs once, the test runs before every pass, and the step runs after every pass. The loop ends the first time the test is falsy.

Where the loop's i lives

A name declared with let in the start of a for belongs to the loop. It exists inside the round brackets and the body, and nowhere else. So two loops can each have their own i, and neither one sees the other's.

for (let i = 0; i < 2; i++) {
  console.log("first loop", i);
}
for (let i = 5; i < 7; i++) {
  console.log("second loop", i);
}
first loop 0
first loop 1
second loop 5
second loop 6

After the loop, i is gone, and reading it is an error (Common mistakes 4). Keep any value you need later in a variable declared before the loop, the way total was. So declare a loop's counter in its start, and a result before the loop.

for...of: one pass per value

Often the job is not "count to n" but "look at every value in this list". JavaScript keeps a list of values in an array, written in square brackets: [4200, 9900, 8000]. Module 7 teaches arrays properly. Here you only need two facts: an array holds values in order, and week.length is how many it holds.

A for...of loop hands you the values one at a time. There is no counter to start, test or step: the loop takes the next value on each pass and stops after the last one.

The loop takes each value of week in order, 4200, 9900, 8000, 7999 and 15000. The if adds 1 to count for the three values of at least 8000, so count ends at 3.

Passstepssteps >= 8000count after the pass
14200false0
29900true1
38000true2
47999false2
515000true3
const week = [4200, 9900, 8000, 7999, 15000];
let count = 0;
for (const steps of week) {
  if (steps >= 8000) {
    count++;
  }
}
console.log(count + " of " + week.length + " days met the goal");
3 of 5 days met the goal

steps is a const, and that is allowed: each pass gets a fresh steps with the next value, so nothing is ever reassigned. A string works the same way, one character per pass. Module 3 shows where "one character" gets subtle.

for (const letter of "Zara") {
  console.log(letter);
}
Z
a
r
a

So for...of is the loop for "every value of this list", and a for with a counter is the loop for "count from here to there". Lesson 04 turns that into a flowchart.

Why a loop stops, and when it never does

A loop stops because its test finally answers falsy. In a for loop the step moves i toward that answer on every pass. If the test is falsy from the very first check, the body runs zero times, and that is not an error.

const n = 0;
for (let i = 1; i <= n; i++) {
  console.log("never printed");
}
console.log("the loop ran 0 times");
the loop ran 0 times

If nothing moves the test toward false, the loop never stops. That is an infinite loop. The while loop below, from lesson 02, forgot to add 1 to i, so i < 5 stays true for ever.

let i = 0;
let total = 0;
while (i < 5) {
  total += i;
}
console.log(total);

Nothing prints. On the Playground, signed out, this program ran for about 10 seconds and then stopped with the verdict Time Limit Exceeded (a run on 2026-10-07). The judge does the same to a submitted program. So every loop needs something inside it that moves its test toward false.

if, else and the two for loops

if (condition) {
  // runs when condition is truthy
} else {
  // runs when condition is falsy (the else part is optional)
}

for (start; test; step) {
  // the body: runs while test is truthy
}

for (const value of list) {
  // one pass per value of the list, in order
}
  • condition and test are any expression, read as truthy or falsy.
  • start runs once; test runs before every pass; step runs after every pass.
  • list is an array or a string; value takes the next item on each pass.
  • The braces mark the block. This track always writes them.
Example 1: hot or not

The smallest decision: one number, one condition, two possible lines.

const celsius = 31;
if (celsius >= 30) {
  console.log("hot: carry water");
} else {
  console.log("not hot");
}
hot: carry water

31 is at least 30, so the if block runs and the else block is skipped. Set celsius to 30 and the same line prints, because >= includes the edge.

Run in Compiler
Example 2: Bob's savings jar

Bob puts 10 taka in a jar on day 1, 20 on day 2, 30 on day 3, and so on for a week. A for loop counts the days and keeps the running total.

let saved = 0;
for (let day = 1; day <= 7; day++) {
  saved += day * 10;
  console.log("day " + day + ": " + saved);
}
day 1: 10
day 2: 30
day 3: 60
day 4: 100
day 5: 150
day 6: 210
day 7: 280

saved is declared before the loop, so it keeps growing across passes. Declared inside the body, it would start again at 0 on every pass.

Run in Compiler
Example 3: Zara's weekly report

The program Zara actually keeps. It reads n, then n daily step counts. It reports the week's total and how many rest days she took, a rest day being under 1000 steps.

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

const n = nextInt();
let total = 0;
let restDays = 0;
for (let day = 1; day <= n; day++) {
  const steps = nextInt();
  total += steps;
  if (steps < 1000) {
    restDays++;
  }
}
out.push("total " + total);
out.push("rest days " + restDays);

console.log(out.join("\n"));
total 52230
rest days 2

That output is for the input 7, then 7500 8000 12030 0 9100 600 15000. The loop reads one count per pass with nextInt(), so it reads exactly n of them. The if decides which passes add to restDays.

Run in Compiler

Where this is used

  • The language standard itself. Array.prototype.includes asks whether an array holds a value. ECMA-262 defines it as a loop, "Repeat, while k < len", with an if inside that returns true on a match.
  • Express, the Node web framework. Express 4's router keeps an app's middleware in a list. It walks the list with a while loop and tests each layer against the request's path before running it.
  • Node's event loop. libuv, the C library under Node, runs it as a while loop in its uv_run function. Each turn handles a batch of events, until nothing is left to wait for (Module 12).
  • Browser games. A game draws a frame, then asks requestAnimationFrame to call it again before the next screen refresh, usually about 60 times a second. Each frame is one pass of a loop, with an if for every key the player holds (Module 14).

Common mistakes

1. A semicolon after the condition.

const steps = 500;
if (steps >= 8000); {
  console.log("Goal met");
}
Goal met

No error, and the wrong answer: 500 steps "met" the goal. The semicolon ends the if with an empty statement, and the braces after it are a separate block that always runs. Delete the semicolon. You will type it because almost every other line ends in one.

2. Two lines under an if without braces.

const steps = 500;
if (steps >= 8000)
  console.log("Goal met");
  console.log("Badge unlocked");
Badge unlocked

Without braces, an if owns only the one statement after it. The indentation suggests the badge line belongs to the if, but JavaScript ignores indentation. Write the braces every time. You will leave them out because the first version had one line, and the second line arrives a week later.

3. const as the counter of a for loop.

for (const i = 0; i < 3; i++) {
  console.log(i);
}
0
TypeError: Assignment to constant variable.

The first pass runs, then the step i++ tries to change a constant, and Node 22 stops the program. A counter changes, so it is a let. You will write const because for...of uses it, and there each pass gets a new name instead of changing the old one.

4. Reading the counter after the loop.

for (let i = 0; i < 3; i++) {
  console.log(i);
}
console.log("finished at " + i);
0
1
2
ReferenceError: i is not defined

The loop's i ended with the loop. If you need a value afterwards, declare a variable before the loop and set it inside. You will try it because the last value of i feels as if it should still be lying around.

Brain teaser

Bob says this loop runs three times, because 10 divided by 3 is about 3.

for (let i = 10; i > 0; i -= 3) {
  console.log(i);
}

How many lines does it print, which numbers, and what is the value of i when the test finally says no?

Write two columns on paper, i and the test i > 0, and fill one row before every pass. Do not divide; step.

Exercise 1Easy

David's rocket game starts with a countdown. Print the numbers from n down to 1, then the word that launches the rocket.

Input. One whole number n.

Output. n lines with n, n - 1, and so on down to 1, then one line Go!.

Constraints. 1 <= n <= 1000.

Sample. Input 3 gives 3, 2, 1 and Go! on four lines.

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));

Not graded on its own. A for loop can count down: start at n, test i >= 1, and step with i--.

Run in Compiler
Exercise 2Easy

Zara's step counter, done properly. For each day, say whether she met her goal, then say how many days she met it.

Input. The first line holds n and the goal g. The second line holds n whole numbers, the steps of each day.

Output. n + 1 lines: for each day in order, met when its steps are at least g, otherwise short; then the number of days met.

Constraints. 1 <= n <= 100000. 0 <= g <= 100000. 0 <= steps <= 100000.

Sample. Input 5 8000 and 7500 8000 12030 0 9100 gives short, met, met, short, met and 3 on six lines.

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));

Graded as steps-goal. The hidden tests include a day that hits the goal exactly, a goal of 0 and a single day, so read "at least" carefully.

Run in Compiler
Exercise 3Medium

Zara wants to know how often she beat the day before. Count the days whose steps are higher than the previous day's. Day 1 has no previous day, so it never counts.

Input. n, then n whole numbers, the steps of each day.

Output. One number: how many days had more steps than the day before.

Constraints. 1 <= n <= 100000. 0 <= steps <= 100000.

Sample. Input 5 and 4200 9900 8000 8100 15000 gives 3: 9900 beat 4200, 8100 beat 8000, and 15000 beat 8100.

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));

Not graded on its own. Keep yesterday's value in a let declared before the loop, and update it at the end of every pass. Then try n = 1 and two equal days.

Run in Compiler
Exercise 4Hard

Zara's app gives a badge for the longest streak: the most days in a row that met the goal. Find the length of her longest streak.

Input. The first line holds n and the goal g. The second line holds n whole numbers, the steps of each day.

Output. One number: the length of the longest run of consecutive days with steps of at least g, or 0 when no day met it.

Constraints. 1 <= n <= 100000. 0 <= g <= 100000. 0 <= steps <= 100000.

Sample. Input 7 8000 and 9000 8000 100 8500 9100 12000 50 gives 3: days 4, 5 and 6.

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));

Not graded on its own. You need two counters: the streak running now, and the best one so far. Decide what each does on a day that misses the goal, and test a week where the best streak is the last one.

Run in Compiler

Common doubts

  • Do I really need braces when the block has one line?

    The language lets you leave them out for a single statement. This track always writes them. So does the Google JavaScript Style Guide, which asks for braces on every control structure, and ESLint's curly rule can enforce it. Common mistakes 2 is the reason: the second line always arrives later.

  • Why do some loops start at 0 and others at 1?

    Both run n passes when the test matches the start. for (let i = 0; i < n; i++) counts 0 to n - 1, which matches the positions in an array (Module 7). for (let day = 1; day <= n; day++) counts 1 to n, which matches how people number days. Pick the one whose numbers you print.

  • Is i++ different from i += 1 as the step?

    Not as the step. The step's own value is thrown away, so both just add 1. Module 2, lesson 02, shows where i++ and ++i differ, which is only when you use the value.

  • What happens when n is 0?

    The test fails at its first check, and the body runs zero times. That is correct behaviour, not an error, and a program should still print what an empty input needs, as Zara would check first.

  • Are for...of and for...in the same loop?

    No. for...of hands you values. for...in hands you the names of an object's properties, and on an array it hands you the positions as strings. Lesson 02 shows both and the trap between them.

Key takeaways

  • if runs its block when the condition is truthy, and else runs its block when it is falsy; this track always writes the braces.
  • A for loop runs its start once, its test before every pass and its step after every pass.
  • A loop ends the first time its test is falsy; a test that is falsy at once means zero passes.
  • A let declared in a loop's start lives only inside that loop; keep results in variables declared before it.
  • for...of takes one value per pass from an array or a string, with no counter to manage.
  • Go deeper: Under the Hood, loops as jumps, the iterator protocol and a loop lab (Pro).

Next, lesson 02 takes every statement one at a time: else if, switch, while, do...while, for...in, break and continue.

End of lesson 1

Mark it done, and your progress moves with you.

Next: Every Statement, One by One