1.Why do we need loops?
In Lesson 1 we broke a program into sequence, conditions and repetition. If the conditionals of Lesson 3 are a fork in the road, the loops in this lesson are a road that circles back. To print 1 to 5 you could write five lines of console.log, but printing 1 to 1,000 is a different story. Copying and pasting lines invites mistakes, and changing a number means editing every line.
A loop lets you write, just once, "keep doing this while this condition holds." People get tired and make mistakes when they repeat the same task for a long time, but a computer does the hundredth repetition exactly like the first. On the other hand, a computer cannot decide on its own when to stop, so a person has to write the stopping condition precisely. Throughout this lesson we practice thinking of "what to repeat" and "when to stop" as a pair.
The code below produces the same result as five separate lines, using a single loop. Change the number 5 to 1000 and it prints 1,000 lines, yet the code changes by only a few characters. That is why loops are the starting point of automation.
for (let i = 1; i <= 5; i++) {
console.log('Greeting #' + i + ': Hello');
}Greeting #1: Hello Greeting #2: Hello Greeting #3: Hello Greeting #4: Hello Greeting #5: Hello
2.The `for` loop — a fixed number of repetitions
Inside the parentheses of a for loop are three parts separated by semicolons. The first is setup that runs only once (let i = 1), the second is a condition checked before every repetition (i <= 5), and the third is what happens at the end of each pass (i++). i++ is shorthand for i = i + 1. The body inside the curly braces runs while the condition is true, and the loop ends the moment the condition becomes false.
Knowing the exact order of execution matters. It goes setup → check condition → body → increment → check condition → body → increment …, and when the condition is false it skips the body and moves on to the code below the loop. That means at the end, i holds the first value that broke the condition. Writing down a variable's value line by line, as in the table below, is called tracing, and it is the first thing to try when a loop behaves strangely.
Naming the loop variable i is just an old convention, not a rule. If the meaning is clearer, use names like month or page. Because the loop variable changes, declare it with let, not const (Lesson 2).
const dan = 2;
for (let i = 1; i <= 4; i++) {
console.log(dan + ' x ' + i + ' = ' + dan * i);
}2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 2 x 4 = 8
| Pass | Value of `i` | Condition `i <= 4` | What happened |
|---|---|---|---|
| 1 | 1 | true | prints 2 x 1 = 2, i becomes 2 |
| 2 | 2 | true | prints 2 x 2 = 4, i becomes 3 |
| 3 | 3 | true | prints 2 x 3 = 6, i becomes 4 |
| 4 | 4 | true | prints 2 x 4 = 8, i becomes 5 |
| - | 5 | false | skips the body, loop ends |
3.`for...of` and two basic patterns — running totals and counting
When you want to take the values out of a list one at a time, for...of is the easiest to read. Write for (const x of list) and the values of list go into x in order, from the first. Since you do not need to keep track of positions, you make fewer numbering mistakes. Lists (arrays) are covered in detail in Lesson 6; for now, just know that they are values separated by commas inside square brackets.
Much of what loops do comes down to two patterns. The running-total (accumulator) pattern creates an "empty so far" value before the loop, such as let sum = 0, and adds to it on every pass. The counting pattern starts with let count = 0 and adds 1 only when a condition is met. In both patterns the starting value matters: a sum starts at 0, a product starts at 1, and joining text starts with the empty string ''.
If you declare the accumulator inside the loop, it is reborn as 0 on every pass and the total never builds up. Always declare it outside the loop, above it. Conversely, the console.log that prints the result usually goes after the loop ends. Put it inside and the running total is printed on every pass. When you want to see intermediate values, you may put it inside on purpose for tracing.
const sales = [12000, 8500, 30000, 4000];
let sum = 0;
let count = 0;
for (const won of sales) {
sum += won;
if (won >= 10000) {
count++;
}
}
console.log('Total', sum);
console.log('10,000 won or more:', count);Total 54500 10,000 won or more: 2
[70, 85, 90, 60], with a loop. In what order should you think it through?const scores = [70, 85, 90, 60];
let total = 0;
for (const s of scores) {
total += s;
}
console.log(total / scores.length);76.25
- Step 1, what to accumulate: the sum of the scores. The starting value is
0. - Step 2, the work of one pass: take one score and add it to the sum. 70 → 155 → 245 → 305.
- Step 3, after the loop ends: divide the sum 305 by the count 4. Get the count with
scores.length. - Check: 305 ÷ 4 = 76.25. It lies between 70 and 90, so it is plausible. An empty list would give 0 ÷ 0, which is
NaN, so that case needs separate handling.
4.The `while` loop and infinite loops — who changes the stop condition?
A while loop takes just one condition and keeps running the body while it is true. It fits well when you do not know in advance how many passes you need and must repeat "until the goal is reached." For example, how many months it takes to reach a savings goal by putting away the same amount each month can be found with a formula, but adding one month at a time and counting is the most intuitive way.
The question to always ask with while is: "Is there a line in the body that changes the condition?" In the code below, without saved += 50000, saved stays 0 forever and the condition stays true forever. That is an infinite loop. A program stuck in an infinite loop keeps running without any error message, so the browser tab seems to stop responding. The playground on this page forcibly stops code that does not finish within 2 seconds and reports a time limit.
There are three checks that prevent infinite loops. Does the variable in the condition change in the body? Does it change in the direction that makes the condition false (starting at 0 and only subtracting moves you away from the goal)? And did you use a condition like <, which also stops when you overshoot, instead of !==, which stops only on an exact match? As we saw with 0.1 + 0.2 in Lesson 2, decimals may never become exactly equal, so stopping on !== is risky.
const goal = 300000;
let saved = 0;
let months = 0;
while (saved < goal) {
saved += 50000;
months++;
}
console.log('After', months, 'months:', saved, 'won');After 6 months: 300000 won
console.log on the first line of the body that prints the loop variable and the accumulator. Once you can see which values appear after how many passes, the cause is usually obvious. If there are too many passes, temporarily make the condition smaller so you only see the first few.5.`break` and `continue` — stopping early and skipping
break exits the loop immediately. When you only need the first value in a list that meets a condition, going all the way to the end after finding it is wasteful, so you stop with break. continue skips the rest of the current pass and moves on to the next one. Use it to filter out empty or invalid values and process only the rest.
Both keywords are handy, but overusing them makes the flow hard to follow. If a single loop mixes several break and continue statements, it is easier to read if you rewrite it with conditionals or split it into functions, which you will learn in Lesson 5. Inside a function in particular, return ends both the loop and the function at once, which shows the meaning "return as soon as you find it" more clearly.
Always think about the "not found" case too. In the first code below, found starts at -1 as a marker meaning "not found yet." If it is still -1 after the loop ends, no value in the list met the condition. The array method indexOf returning -1 when it finds nothing follows the same convention (Lesson 6).
const temps = [3, 1, -2, 5, -4];
let found = -1;
for (let i = 0; i < temps.length; i++) {
if (temps[i] < 0) {
found = i;
break;
}
}
console.log('First below-zero position', found);First below-zero position 2
const items = ['apple', '', 'pear', '', 'plum'];
for (const name of items) {
if (name === '') {
continue;
}
console.log(name);
}apple pear plum
6.Off-by-one — the most common mistake, wrong by exactly one
The most common loop bug is running one time too many or one time too few. It is called an off-by-one error. There are usually three causes: whether to start at 0 or 1, whether to use < or <= in the condition, and confusing "how many" with "the last number." Array positions count from 0, so the last position of an array of length 5 is 4, not 5.
Let's build intuition with the fence-post problem. If you put posts every 1 m along a 10 m fence, you need 11 posts, not 10. The number of gaps and the number of end points differ by one. In the same way, for (let i = 0; i < 5; i++) runs 5 times with 0, 1, 2, 3, 4; for (let i = 1; i <= 5; i++) runs 5 times with 1 to 5; and for (let i = 0; i <= 5; i++) runs 6 times with 0 to 5. They all contain a "5," but the counts differ.
The surest way to catch off-by-one errors is to test the boundary values yourself. Just as we checked the boundaries of conditionals in Lesson 3, always try a loop with zero items, one item, and the last item. If you loop over an array by position and overshoot by one with i <= arr.length, arr[i] becomes undefined on the last pass and the total turns into NaN. No error message appears, just a strange value, which makes it even harder to find. If you do not need positions, using for...of avoids this mistake altogether.
<= and < — the extra slot is undefined, the total is NaNconst nums = [10, 20, 30];
let a = 0;
for (let i = 0; i <= nums.length; i++) {
a += nums[i];
}
console.log('Overshot total', a);
let b = 0;
for (let i = 0; i < nums.length; i++) {
b += nums[i];
}
console.log('Correct total', b);Overshot total NaN Correct total 60
- Does the result make sense when the list is empty (0 items)? Total 0, count 0, and the average handled separately
- With only one value, does the loop run exactly once?
- Is the last value processed? Check whether the condition is
<or<= - Does the variable in the condition change in the body so that it eventually becomes false?
- Is the accumulator declared outside the loop?
7.Reading and judging loops — when facing AI-written code
Ask an AI coding assistant to "sum this list" and you quickly get code with a loop in it. But you have to check for yourself whether that code runs correctly on your data. When reading a loop, look for just three things: what it prepares before the loop (the starting value), what it does in one pass (the body), and when it stops (the stop condition). If you can put each of these three into one sentence, you understand the code.
Next, trace it by hand with a small input. Feed in a list of about three values, write down the variables on each pass like the trace table above, and compare with the actual output. Also try an empty list and a one-item list. If the results differ, tell the AI specifically: "With this input I got this result, but I expected this." That makes fixing it much faster. Reading errors and narrowing down causes is covered in Lesson 8, and verifying AI-written code in Lesson 10.
📌 Key points
- A loop is syntax that states "what to repeat" and "when to stop" as a pair
- A fixed number of times:
for; each value in a list:for...of; until a goal is reached:while - Accumulating: set a starting value before the loop (sum 0) and add to it; counting: add 1 only when a condition holds
- A
whileloop without a line in the body that changes the condition is an infinite loop <vs<=, starting at 0 vs 1 — test the boundaries with an empty list, one value and the last value
🧪 Code lab
Write functions that use loops to find totals, counts and positions. Boundary tests such as empty lists and 0 are graded too, so think about those cases before you run your code.
Your code runs only inside an isolated sandbox in this browser and is never sent to a server. It has no network access and is stopped after 2 seconds. Edited code is saved only in this browser. Ctrl+Enter (⌘+Enter) runs it; Tab inserts two spaces (press Esc, then Tab, to move on).
JavaScript is off, so the code can't run here, but you can still read each task, its starter code, the automatic checks and a sample solution.
1Add 1 through n
Write a function sumTo(n) that takes a number n and uses a loop to return the sum from 1 to n. If n is 0, return 0. Example: sumTo(5) is 15.
sumTo(5)expected15sumTo(1)expected1sumTo(0)expected0sumTo(100)expected5050
💡 Hint
If you write the condition as i < n, the final n is left out. What should you write to include n?
Show a sample solution
function sumTo(n) {
let sum = 0;
for (let i = 1; i <= n; i++) {
sum += i;
}
return sum;
}2Count scores at or above a cutoff
Write a function countAtLeast(scores, cut) that takes an array of scores scores and a cutoff score cut, and returns how many scores are at or above the cutoff. For an empty array, return 0.
countAtLeast([50,70,90], 70)expected2countAtLeast([], 60)expected0countAtLeast([60,60,59], 60)expected2countAtLeast([10,20], 100)expected0countAtLeast([0,0], 0)expected2
💡 Hint
"At or above" includes equal values. Check the difference between > and >=.
Show a sample solution
function countAtLeast(scores, cut) {
let count = 0;
for (const s of scores) {
if (s >= cut) {
count++;
}
}
return count;
}3Find the position of the first negative number
Write a function firstNegative(nums) that returns the position (counting from 0) of the first negative number in the array nums. If there is no negative number or the array is empty, return -1. Once you find one, stop instead of continuing the loop.
firstNegative([3,1,-2,5,-4])expected2firstNegative([-1,2])expected0firstNegative([1,2,3])expected-1firstNegative([])expected-1firstNegative([0,4,-7])expected2
💡 Hint
Inside a function, return ends both the loop and the function at once. Put the "not found" return -1 after the loop ends.
Show a sample solution
function firstNegative(nums) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] < 0) {
return i;
}
}
return -1;
}4Sum of the digits (while)
Write a function digitSum(n) with while that takes a non-negative integer n and returns the sum of its digits. Example: digitSum(1234) is 1+2+3+4 = 10. n % 10 is the last digit, and Math.floor(n / 10) is the number with the last digit removed.
digitSum(1234)expected10digitSum(0)expected0digitSum(9)expected9digitSum(1000)expected1digitSum(99999)expected45
💡 Hint
If you forget the line that shrinks n, you get an infinite loop. Check that the variable in the condition changes in the body.
Show a sample solution
function digitSum(n) {
let sum = 0;
while (n > 0) {
sum += n % 10;
n = Math.floor(n / 10);
}
return sum;
}🤖 Try asking AI like this
Copy a prompt and replace the [ ] parts with your own situation. Don't take the answer on trust — check it against this lesson.
When you don't understand how a loop runs
Suppose the JavaScript loop below runs with the input [small example input]. Show me how the variables change on each pass as a trace table (pass, variables, condition true/false, what happened). Also point out the condition at the moment the loop ends. Code: [paste here]
When you want to verify loop code an AI gave you
Explain the starting value, body and stop condition of the following loop in one sentence each. Then make a table of boundary test inputs and expected results from the angles of an empty list, a single value, the last value and possible infinite loops. I will run them myself and compare. Code: [paste here]
When you need more loop practice
Make 5 JavaScript loop exercises (for, while, for...of, break, continue), from easiest to hardest. Be sure to include one running-total/counting pattern and one off-by-one trap, and use fictional data. Hide the answers, and when I send my code, grade it with boundary-value inputs.
🧰 Related tools
Tools for checking and tidying the JSON, regular expressions and text from this lesson. Don't paste real personal data or passwords.
- Text Diff CheckerPaste the output from before and after fixing your code to compare them, and you can immediately spot the line where a loop ran once too many or too few.
- Word Frequency AnalyzerA tool that counts how many times each word appears, good for getting a feel for what this lesson's counting pattern produces in practice.
- Quick NotepadUse it as a notepad for writing hand-trace tables or keeping practice code for a while.
- MDN Web Docs — JavaScript Guide: Loops and iteration
- MDN Web Docs — reference for the for, while, for...of, break and continue statements
- General material on iteration statements in the ECMAScript Language Specification (ECMA-262)
Reached every goal above? Mark the lesson complete.
Storage is unavailable in this browser, so this lasts only for this page.💻 Coding Basics
- 1What Is a Program? — Breaking Work into Sequence, Choice, and Repetition
- 2Values, Variables, and Types — Putting Name Tags on Values
- 3Conditionals — Taking Different Paths Depending on the Situation
- 4Loops — Doing the Same Thing Many Times, Exactly
- 5Functions — Splitting Work into Small Named Machines
- 6Arrays and Objects — Storing and Handling Data
- 7Strings and Text — Cutting, Finding and Replacing Characters
- 8Debugging and Reading Errors — Turning Red Text into Clues
- 9Simple Automation — Spreadsheet Math and Text Cleanup in Code
- 10Reading and Verifying AI-Written Code — Tests, Security, Licenses, Privacy