In the previous lesson you got the program to make decisions. The problem is that it decided about a single task: to classify the urgency of the six tasks in Taller Nómada's backlog you would have to copy the same block six times, and when Marta adds the seventh you would have to touch the code again. A loop solves exactly that: you write the instructions once and tell the program how many times to repeat them. In this lesson you will meet JavaScript's three classic loops —for, while and do...while—, you will learn to walk through a list of tasks by index, and you will master the four patterns that solve 90% of real problems: counting, accumulating, finding the maximum and checking whether something exists.
Contents
- What a loop is and why you need one
- Anatomy of the
forloop - The
whileloop - The
do...whileloop - Which one to choose: a comparison table
- Walking a list by index with
.length - Taller Nómada's backlog
- The accumulator pattern: total backlog hours
- The counter pattern: how many tasks each person carries
- The maximum/minimum pattern: the most urgent task
- The flag pattern: is there any overdue task?
- Infinite loops and how to avoid them
for...ofandfor...in: a preview- Common Mistakes and Tips
- Exercises
- Conclusion
- What a loop is and why you need one
A loop (or iteration) is a control structure that runs a block of statements repeatedly while a condition holds. Each full pass through the block is called an iteration.
Without loops, the backlog summary is written like this:
// ✗ Unsustainable
console.log(`1. ${title1} · ${hours1} h`);
console.log(`2. ${title2} · ${hours2} h`);
console.log(`3. ${title3} · ${hours3} h`);
// ...and so on, up to fortyWith a loop you write it once and it works with six tasks, with forty or with none. On top of that, if tomorrow you have to add the priority to the summary, you change one line instead of forty.
- Anatomy of the
for loop
for loopThe for is the go-to loop when you know in advance how many times to repeat or when you are walking a list from start to finish. Its header contains three parts separated by ;:
A minimal example: printing the first three task numbers.
Output:
What each part does, and in what order:
| Part | When it runs | In the example |
|---|---|---|
| Initialization | Once only, before everything else | let i = 1 |
| Condition | Before each iteration | i <= 3 |
| Body | If the condition was true | console.log(...) |
| Update | After each iteration | i++ |
The full flow, step by step:
flowchart TD
A["Initialization<br/>let i = 1"] --> B{"Condition<br/>i <= 3"}
B -->|false| F["End of the loop"]
B -->|true| C["Body<br/>console.log(...)"]
C --> D["Update<br/>i++"]
D --> B
Follow the trace in your head:
| Step | i |
Condition i <= 3 |
What happens |
|---|---|---|---|
| 1 | 1 | true |
Prints "1", then i becomes 2 |
| 2 | 2 | true |
Prints "2", then i becomes 3 |
| 3 | 3 | true |
Prints "3", then i becomes 4 |
| 4 | 4 | false |
The loop ends |
Two fundamental observations:
- The condition is checked BEFORE each iteration, including the first. If you start with
let i = 5and the condition isi <= 3, the body does not run even once. That is correct and desirable: a loop over an empty list must do zero iterations. - The update runs AFTER the body, not before. That is why
iis still 1 during the first iteration.
2.1 The control variable
By convention it is called i (for index), and j, k if there are more. It is declared with let, never with const: it has to be able to change on every update.
Declaring it in the header with let means it only exists inside the loop, which is exactly what you want:
for (let i = 0; i < 3; i++) {
console.log(i);
}
console.log(i); // ✗ ReferenceError: i is not definedIf you need to know the counter's final value after the loop, declare the variable outside. But you almost never need to: it is a sign that what you really wanted was a while.
2.2 Variations on the header
The three parts are optional, although the ; are not. These variants are legal:
let i = 0;
for (; i < 3; i++) { } // no initialization
for (let j = 10; j > 0; j -= 2) { } // counting down in steps of 2
for (let k = 0, total = 0; k < 3; k++) { } // two variables, separated by a commaCounting backwards is common when order matters:
- The
while loop
while loopwhile repeats a block as long as a condition is true. It has no initialization or update of its own: that is up to you.
let pendingHours = 45;
let days = 0;
const HOURS_PER_DAY = 8;
while (pendingHours > 0) {
pendingHours -= HOURS_PER_DAY;
days++;
}
console.log(`The open backlog takes ${days} days of work.`);
// The open backlog takes 6 days of work.It is exactly the same mechanism as a for with the three parts spread out:
let i = 0; // initialization: outside
while (i < 3) { // condition: in the header
console.log(i);
i++; // update: last line of the body
}So what is while for, if for does the same thing? For the cases where you do not know how many iterations will be needed. In the hours example we do not know in advance how many days it will come to: it depends on the total. Writing it with for would force you to calculate the number of iterations first, which is precisely what we wanted to find out.
- The
do...while loop
do...while loopdo...while is identical to while with one decisive difference: the body runs at least once, because the condition is checked at the end.
let attempts = 0;
let taskFound = false;
do {
attempts++;
console.log(`Sync attempt no. ${attempts}`);
taskFound = attempts === 3; // we pretend the third attempt is the lucky one
} while (!taskFound && attempts < 5);
console.log(`Synced after ${attempts} attempt(s).`);Notice the final ;: in do...while it is mandatory after the while, because the statement ends there. It is the only one of the three that has it.
The difference from while shows up with a condition that is false from the start:
let n = 10;
while (n < 5) {
console.log('while: I never run');
}
do {
console.log('do...while: I run once');
} while (n < 5);Output:
flowchart LR
subgraph W["while"]
direction TB
W1{"Condition?"} -->|true| W2["Body"]
W2 --> W1
W1 -->|false| W3["End"]
end
subgraph D["do...while"]
direction TB
D1["Body"] --> D2{"Condition?"}
D2 -->|true| D1
D2 -->|false| D3["End"]
end
do...while is the least used of the three. Its niche is processes that always have to be attempted once before you decide whether to repeat them: asking for a value until it is valid, retrying a failed operation, generating an identifier until it is not a duplicate.
- Which one to choose: a comparison table
| Criterion | for |
while |
do...while |
|---|---|---|---|
| Number of iterations known | ✓ Ideal | △ Possible | ✗ Rare |
| Number of iterations unknown | △ Possible | ✓ Ideal | ✓ If it runs at least once |
| The body runs a minimum of | 0 times | 0 times | 1 time |
| The condition is checked | At the start | At the start | At the end |
| Counter visible in the header | ✓ Yes | ✗ No | ✗ No |
| Walking a whole list | ✓ The natural choice | △ Verbose | ✗ Fails with an empty list |
| Risk of an infinite loop | Low | Medium | Medium |
Needs a final ; |
No | No | Yes |
Practical rule: if you are walking a list or repeating a known number of times, for. If you are repeating "until something happens", while. And if that something can only be evaluated after the first attempt, do...while.
- Walking a list by index with
.length
.lengthTo work with the backlog you need to store several values in a single variable. That is an array: an ordered list of values, written between square brackets and separated by commas.
const titles = [
'Redesign the multipurpose room',
'Signage for the screen-printing workshop',
'Update the bookings website'
];For now you only need three things from arrays; the rest arrives in Arrays: Basics and Methods:
| What I need | How it is written | Example |
|---|---|---|
| How many elements there are | array.length |
titles.length → 3 |
| The element at position N | array[N] |
titles[0] → 'Redesign the multipurpose room' |
| Walking through all of them | A for with an index |
for (let i = 0; i < titles.length; i++) |
Indexes start at 0. An array of 3 elements has indexes 0, 1 and 2. The last index is always length - 1, and array[length] gives undefined, not an error. This is the number-one cause of bugs with loops.
Output:
1. Redesign the multipurpose room 2. Signage for the screen-printing workshop 3. Update the bookings website
That header —let i = 0, i < array.length, i++— is the most repeated pattern in all of JavaScript. Make it second nature:
- It starts at
0because that is the first index. - It uses
<and not<=, becauselengthis the number of elements, not the last index. - The
i + 1in the message is purely cosmetic: it turns the index into a readable position number for Marta.
- Taller Nómada's backlog
You do not know how to create objects yet, so we will represent the six tasks with parallel arrays: one array per field, where position i in all of them describes the same task. This code will be the starting point for the following examples and for those in lessons 02-03 and 02-04.
const TODAY = '2026-09-20';
const ids = [1, 2, 3, 4, 5, 6];
const titles = [
'Redesign the multipurpose room',
'Signage for the screen-printing workshop',
'Update the bookings website',
'Screen-printing ink inventory',
'Bookbinding guide for residents',
'Carpentry workshop quote'
];
const assignees = ['Iván', 'Marta', 'Lucía', 'Marta', 'Iván', 'Iván'];
const priorities = ['high', 'medium', 'high', 'low', 'medium', 'high'];
const statuses = ['in-progress', 'pending', 'pending', 'done', 'in-progress', 'pending'];
const hours = [12, 6, 14, 3, 8, 5];
const dueDates = [
'2026-09-30', '2026-10-15', '2026-10-02',
'2026-09-12', '2026-11-05', '2026-09-05'
];Reading task number 3 means looking at position 2 in every array: titles[2], assignees[2], hours[2]... Here is the full listing:
| i | id | Title | Assignee | Priority | Status | Hours | Due date |
|---|---|---|---|---|---|---|---|
| 0 | 1 | Redesign the multipurpose room | Iván | high | in-progress | 12 | 2026-09-30 |
| 1 | 2 | Signage for the screen-printing workshop | Marta | medium | pending | 6 | 2026-10-15 |
| 2 | 3 | Update the bookings website | Lucía | high | pending | 14 | 2026-10-02 |
| 3 | 4 | Screen-printing ink inventory | Marta | low | done | 3 | 2026-09-12 |
| 4 | 5 | Bookbinding guide for residents | Iván | medium | in-progress | 8 | 2026-11-05 |
| 5 | 6 | Carpentry workshop quote | Iván | high | pending | 5 | 2026-09-05 |
Parallel arrays are a temporary and fragile solution. If you sort one and not the others, the data ends up scrambled with no warning. The correct representation is an array of objects, and it arrives in Module 4. In the meantime they work perfectly well for learning loops, and suffering their fragility will make you appreciate objects when they turn up.
A full listing of the backlog combining a loop and a conditional:
for (let i = 0; i < ids.length; i++) {
const badge = statuses[i] === 'done' ? '✓' : '○';
const alert = dueDates[i] < TODAY && statuses[i] !== 'done' ? ' ⚠ OVERDUE' : '';
console.log(
`${badge} [${ids[i]}] ${titles[i]} · ${assignees[i]} · ${priorities[i]} · ${hours[i]} h${alert}`
);
}Output:
○ [1] Redesign the multipurpose room · Iván · high · 12 h ○ [2] Signage for the screen-printing workshop · Marta · medium · 6 h ○ [3] Update the bookings website · Lucía · high · 14 h ✓ [4] Screen-printing ink inventory · Marta · low · 3 h ○ [5] Bookbinding guide for residents · Iván · medium · 8 h ○ [6] Carpentry workshop quote · Iván · high · 5 h ⚠ OVERDUE
There you have the module's two structures working together: the loop walks, the conditional decides. Task 4 is not flagged as overdue even though its date has passed, because it is done: rule R10 requires both conditions.
- The accumulator pattern: total backlog hours
The accumulator is a variable declared before the loop that keeps adding (or concatenating) on each iteration.
let totalHours = 0;
let openHours = 0;
for (let i = 0; i < hours.length; i++) {
totalHours += hours[i];
if (statuses[i] !== 'done') {
openHours += hours[i];
}
}
console.log(`Total estimated hours: ${totalHours} h`); // 48 h
console.log(`Open work hours: ${openHours} h`); // 45 hThe three elements of the pattern:
- Initialize it outside the loop, with the neutral value:
0for sums,1for products,''for text. If you declare it inside, it resets on every iteration and the result is always the last value. - Accumulate inside, with
+=. - Use it afterwards, once the loop has finished.
Declaring it with let is mandatory: const would prevent reassigning it.
An accumulator with a condition —like openHours— is the basis of all of Marta's reports: adding up only what meets a criterion. In Module 4 this will be written in one line with reduce, but the mechanism you learn here is exactly the same.
- The counter pattern: how many tasks each person carries
A counter is an accumulator that adds 1 instead of a value. With three people on the team, three counters and an else if chain are enough:
let ivanTasks = 0;
let martaTasks = 0;
let luciaTasks = 0;
for (let i = 0; i < assignees.length; i++) {
if (assignees[i] === 'Iván') {
ivanTasks++;
} else if (assignees[i] === 'Marta') {
martaTasks++;
} else if (assignees[i] === 'Lucía') {
luciaTasks++;
} else {
console.warn(`Unrecognized assignee on task ${ids[i]}: ${assignees[i]}`);
}
}
console.log(`Iván: ${ivanTasks} task(s)`); // Iván: 3 task(s)
console.log(`Marta: ${martaTasks} task(s)`); // Marta: 2 task(s)
console.log(`Lucía: ${luciaTasks} task(s)`); // Lucía: 1 task(s)The split confirms the diagnosis from lesson 01-08: Iván is overloaded and Lucía has room to spare. Exactly the kind of information Marta could not see with her wall of sticky notes.
This code has an obvious smell: three nearly identical variables and an else if chain that grows with every new person. If someone joins the team tomorrow, you have to touch four places. The clean solution is nested loops over an array of assignees (lesson 02-04) and, definitively, a counter object in Module 4. Recognizing the awkwardness now is part of the learning.
Combining a counter and an accumulator gives us Iván's real workload, which rule R7 needs (nobody exceeds 40 h):
let ivanHours = 0;
for (let i = 0; i < assignees.length; i++) {
if (assignees[i] === 'Iván' && statuses[i] !== 'done') {
ivanHours += hours[i];
}
}
console.log(`Iván's open workload: ${ivanHours} h out of 40`); // 25 h out of 40
if (ivanHours > 40) {
console.error('R7 violated: Iván exceeds 40 hours per week.');
} else {
console.log(`Room available: ${40 - ivanHours} h`); // Room available: 15 h
}
- The maximum/minimum pattern: the most urgent task
To find the extreme value in a list you keep a provisional candidate and replace it every time a better one turns up. We are looking for the open task whose due date is the nearest:
let earliestDate = '';
let mostUrgentTitle = '';
let mostUrgentId = 0;
for (let i = 0; i < dueDates.length; i++) {
const isCandidate = statuses[i] !== 'done';
const isTheFirstOne = earliestDate === '';
const isDueEarlier = dueDates[i] < earliestDate;
if (isCandidate && (isTheFirstOne || isDueEarlier)) {
earliestDate = dueDates[i];
mostUrgentTitle = titles[i];
mostUrgentId = ids[i];
}
}
console.log(`Most urgent: [${mostUrgentId}] ${mostUrgentTitle} (due ${earliestDate})`);
// Most urgent: [6] Carpentry workshop quote (due 2026-09-05)The three keys to the pattern:
- The initial value must be impossible or must mark "no candidate yet". Here we use
'', and theisTheFirstOnevariable detects that case. The usual alternative —initializing with the first element— does not work here because the first element could be done and therefore discarded. - Comparing ISO dates as text works, as you have known since lesson 01-08.
- You have to carry all of the winner's data across at once. Since the fields live in parallel arrays, when you find a better candidate you have to copy its date, its title and its id in the same block. Forgetting one produces a mixed-up result: one task's date with another task's title. That is the fragility of parallel arrays in action.
For the maximum the pattern is identical with the comparison flipped. The task with the most hours:
let maxHours = -1;
let longestTitle = '';
for (let i = 0; i < hours.length; i++) {
if (hours[i] > maxHours) {
maxHours = hours[i];
longestTitle = titles[i];
}
}
console.log(`The longest one: ${longestTitle} (${maxHours} h)`);
// The longest one: Update the bookings website (14 h)-1 as the initial value is safe because rule R3 guarantees that no task has negative hours. Initializing to 0 would work too, but -1 makes it clear that it is a sentinel and not a piece of data.
- The flag pattern: is there any overdue task?
A flag is a boolean variable that starts at false and is set to true as soon as what you are looking for happens. It answers yes-or-no questions about the whole list.
let hasOverdue = false;
let overdueCount = 0;
for (let i = 0; i < dueDates.length; i++) {
if (dueDates[i] < TODAY && statuses[i] !== 'done') {
hasOverdue = true;
overdueCount++;
}
}
if (hasOverdue) {
console.error(`Warning: ${overdueCount} overdue task(s) on the board.`);
} else {
console.log('No overdue tasks. Everything on schedule.');
}
// Warning: 1 overdue task(s) on the board.The opposite flag —checking that all of them satisfy something— is initialized the other way round, at true, and set to false as soon as a counterexample turns up:
let allAssigned = true;
for (let i = 0; i < assignees.length; i++) {
if (assignees[i] === null) {
allAssigned = false;
}
}
console.log(allAssigned ? 'Every task has an assignee.' : 'There are unassigned tasks.');| Question | Initial value | It changes to | When |
|---|---|---|---|
| Is there any that satisfies X? | false |
true |
On finding the first one that satisfies it |
| Do all of them satisfy X? | true |
false |
On finding the first one that does not |
In both cases the loop keeps walking the list even though the answer is already known. That is wasted work, and in lesson 02-04 you will learn to cut it short with break.
- Infinite loops and how to avoid them
An infinite loop is one whose condition never becomes false. In the browser the tab freezes; in Node.js the process hangs until you kill it with Ctrl + C.
The three usual causes:
Forgetting the update.
Updating in the wrong direction.
Modifying the counter inside the body without realizing.
// ✗ i goes back to 0 at the first unassigned task and never gets out
for (let i = 0; i < assignees.length; i++) {
if (assignees[i] === null) {
i = 0;
}
}How to protect yourself:
- Check before running that the condition can become false. Find which variable appears in the condition and verify that the body or the update moves it toward the end.
- Do not modify the control variable inside the body. If you think you need to, you almost certainly wanted a
while. - Add a safety limit in loops whose condition depends on external data:
let attempts = 0;
const MAX_ATTEMPTS = 1000;
while (!hasOverdue && attempts < MAX_ATTEMPTS) {
attempts++;
// ...
}
if (attempts === MAX_ATTEMPTS) {
console.error('Safety limit reached: check the loop condition.');
}- If you have already frozen the tab, close it and use
node file.jswithCtrl + Cto test dubious loops: it is far easier to stop.
There is one legitimate exception: while (true) with an explicit exit inside. It is used in retry loops and it always carries a break, which you will see in the next lesson.
for...of and for...in: a preview
for...of and for...in: a previewModern JavaScript has two more loops, designed to walk collections without managing the index by hand. We introduce them here so you can recognize them; the detail about arrays is in Iterating over Arrays.
for...of walks the values of an array (or of anything iterable, such as a string):
Cleaner than the classic for when you only need the value and not the position. Notice that the variable can be const, because it is created afresh on each iteration. The problem in our case is that with parallel arrays we do need the index in order to look up the other fields, so we will keep using the classic for throughout this module.
for...in walks the keys (in an array, the indexes, and moreover as text):
for (const i of titles) { } // values
for (const i in titles) { // indexes, but '0', '1', '2'... as strings
console.log(i + 1); // ✗ '01', '11', '21' — it concatenates, it does not add
}That concatenation trap is the main reason for the universal rule: for...in is for objects, not for arrays. It also walks inherited properties and does not guarantee the order. When you get to objects in Module 4 you will find its proper place.
| Loop | What it gives you | Use it for |
|---|---|---|
for (let i = 0; ...) |
The numeric index | Arrays when you need the position |
for...of |
The value | Arrays and strings when you only want the value |
for...in |
The key as text | Objects, not arrays |
Common Mistakes and Tips
Using <= with .length.
On the last pass titles[6] is undefined, and the summary prints "undefined · undefined". With .length it is always <.
Starting at 1. for (let i = 1; i < titles.length; i++) silently skips the first task. Indexes start at 0.
Declaring the accumulator inside the loop.
for (let i = 0; i < hours.length; i++) {
let total = 0; // ✗ it resets on every pass
total += hours[i];
}The accumulator always ends up equal to the last element, and on top of that it disappears on the way out. It goes before the loop.
Confusing the index with the value. hours[i] is the number of hours; i is the position. totalHours += i adds up 0+1+2+3+4+5 = 15, a number that looks plausible and is not. When a total comes out strange, check this first.
Modifying .length inside the loop. Adding or removing elements while you walk changes the condition on the fly and produces skipped items or infinite loops. Walk in order to read; build the modified list separately.
Forgetting the ; in do...while. It is the only one that has it, and the ASI you saw in lesson 01-04 does not always save you.
Tip: name the control variable well. i is universally accepted for indexes, but if the loop is long, let taskIndex reads better three months later.
Tip: if the loop body goes past twenty lines, something is wrong. In Module 3 you will learn to extract that logic into functions; for now, at least pull the calculations out into named variables before the if.
Tip: print the trace when something does not add up. A console.log(i, titles[i], totalHours) as the first line of the body shows you the state on every pass and solves most bugs in seconds.
Exercises
Exercise 1 — Workshop summary
Starting from the backlog in section 7, write a single for loop that calculates and displays:
- The total number of tasks.
- How many are
pending, how manyin-progressand how manydone. - The progress percentage, understood as done tasks over the total, rounded to one decimal place.
Remember from lesson 01-06 that Math.round(x * 10) / 10 rounds to one decimal place.
Exercise 2 — Due-date countdown
Write a while loop that, starting from openHours = 45 and an 8-hour working day, calculates how many working days are needed to empty the backlog, printing on each pass the hours still left. Then answer: why is while a better choice here than for?
Exercise 3 — Highest-weight task
Using the priority-to-weight conversion from the previous lesson (high = 3, medium = 2, low = 1), calculate for each open task an effort index equal to weight * hours, and find the open task with the highest index. Display the full listing with its index and, at the end, the winner.
Solutions
Exercise 1
let pending = 0;
let inProgress = 0;
let done = 0;
for (let i = 0; i < statuses.length; i++) {
if (statuses[i] === 'pending') {
pending++;
} else if (statuses[i] === 'in-progress') {
inProgress++;
} else if (statuses[i] === 'done') {
done++;
} else {
console.warn(`Unknown status on task ${ids[i]}: ${statuses[i]}`);
}
}
const total = statuses.length;
const progress = Math.round((done / total) * 100 * 10) / 10;
console.log(`Total tasks: ${total}`); // 6
console.log(`Pending: ${pending}`); // 3
console.log(`In progress: ${inProgress}`); // 2
console.log(`Done: ${done}`); // 1
console.log(`Progress: ${progress} %`); // 16.7 %Three counters updated in the same pass: walking the list once and calculating everything you need in that single pass is always preferable to writing three loops. The final else protects against unforeseen statuses, applying the rule from lesson 02-01 about always closing your chains.
The percentage calculation deserves attention: done / total gives 0.1666...; multiplying by 100 gives 16.666...; and the trick of multiplying by 10, rounding and dividing by 10 leaves 16.7 as a number. toFixed(1) would give the text '16.7', and if you later added it to something else you would get a concatenation instead of a sum.
Exercise 2
let openHours = 45;
const HOURS_PER_DAY = 8;
let days = 0;
while (openHours > 0) {
days++;
openHours -= HOURS_PER_DAY;
const remaining = openHours > 0 ? openHours : 0;
console.log(`Day ${days}: ${remaining} h left`);
}
console.log(`The open backlog takes ${days} working day(s).`);Output:
Day 1: 37 h left Day 2: 29 h left Day 3: 21 h left Day 4: 13 h left Day 5: 5 h left Day 6: 0 h left The open backlog takes 6 working day(s).
The ternary avoids showing -3 h on the last pass, because subtracting 8 from 5 goes past zero.
Why while: the number of iterations is not known before you start; it is precisely what the loop calculates. With a for you would have to write for (let d = 0; d < Math.ceil(45 / 8); d++), that is, solve the problem in the header and then pretend to solve it in the body. The rule holds: when the stopping condition depends on a state that evolves, use while.
Exercise 3
let maxEffort = -1;
let winnerTitle = '';
for (let i = 0; i < ids.length; i++) {
if (statuses[i] === 'done') {
console.log(`[${ids[i]}] ${titles[i]} — closed, does not score`);
} else {
let weight;
if (priorities[i] === 'high') {
weight = 3;
} else if (priorities[i] === 'medium') {
weight = 2;
} else {
weight = 1;
}
const effort = weight * hours[i];
console.log(`[${ids[i]}] ${titles[i]} — effort ${effort}`);
if (effort > maxEffort) {
maxEffort = effort;
winnerTitle = titles[i];
}
}
}
console.log(`\nHighest effort: ${winnerTitle} (${maxEffort})`);Output:
[1] Redesign the multipurpose room — effort 36 [2] Signage for the screen-printing workshop — effort 12 [3] Update the bookings website — effort 42 [4] Screen-printing ink inventory — closed, does not score [5] Bookbinding guide for residents — effort 16 [6] Carpentry workshop quote — effort 15 Highest effort: Update the bookings website (42)
Three details worth noticing. First, let weight; is declared inside the loop on purpose: it is a different value on each iteration, not an accumulator, so it must be reset. Second, maxEffort and winnerTitle do go outside, because they have to survive between passes. And third, this chain of three else if repeated on every iteration is exactly the case that the switch in the next lesson will solve more compactly.
Conclusion
You now know how to repeat. You know the for with its triple header —initialization, condition and update— and the exact order in which they run; the while, for when you do not know how many passes will be needed; and the do...while, for what has to be attempted at least once. You know how to walk a list with for (let i = 0; i < array.length; i++), which is the most-written header in all of JavaScript, and why indexes go from 0 to length - 1.
Above all, you have mastered the four patterns that solve almost any traversal: the accumulator for adding up hours, the counter for splitting tasks per person, the maximum/minimum for finding the most urgent one and the flag for answering whether any is overdue. All four share the same structure: a variable declared before the loop, updated inside and read afterwards. And you know how to spot and avoid an infinite loop before you freeze the tab.
Applied to Nómada Tasks, you can already generate the report Marta never had: 48 estimated hours in total, 45 of open work, 16.7% progress, one overdue task and a very uneven workload —Iván with three tasks and 25 hours against Lucía's single one—.
You will have noticed two awkward spots. One: converting priority into a weight forced you to repeat the same else if chain over and over. Two: the loops keep walking the whole list even when the answer is already known. The first is tackled in Switch Statements, where you will learn a construct designed exactly for choosing between several values of the same variable, and the second in the next lesson, with break and continue.
JavaScript Course: From Beginner to Advanced
Module 1: Introduction to JavaScript
- What Is JavaScript?
- Setting Up Your Development Environment
- Your First JavaScript Program
- JavaScript Syntax and Basic Concepts
- Variables and Data Types
- Basic Operators
- Type Conversion and Comparisons
- The Course Project: Nómada Tasks
Module 2: Control Structures
- Conditional Statements
- Loops: for, while, do-while
- Switch Statements
- Flow Control: break, continue and Nested Loops
- Error Handling with try-catch
Module 3: Functions
- Defining and Calling Functions
- Function Expressions and Arrow Functions
- Parameters and Return Values
- Scope and Closures
- Hoisting and the Execution Context
- Higher-Order Functions
- Recursion
Module 4: Objects and Arrays
- Introduction to Objects
- Object Methods and the
thisKeyword - Arrays: Basics and Methods
- Iterating over Arrays
- Searching, Sorting and Aggregating Data: find, sort and reduce
- Array Destructuring
- Object Destructuring, Spread and Rest
- JSON and Copying Objects
Module 5: Advanced Objects and Functions
- Prototypes and Inheritance
- Classes and Object-Oriented Programming
- Encapsulation: Getters, Setters and Private Fields
- Modules: Import and Export
- Asynchronous JavaScript: Callbacks
- Promises and Async/Await
- The Event Loop and the Microtask Queue
- Iterators and Generators
Module 6: The Document Object Model (DOM)
- Introduction to the DOM
- Selecting and Manipulating DOM Elements
- Handling Events
- Propagation, Delegation and Custom Events
- Creating and Removing DOM Elements
- Rendering Lists and HTML Templates
- Handling and Validating Forms
Module 7: Browser APIs and Advanced Topics
- Local and Session Storage
- The Fetch API and AJAX
- Robust Requests: Errors, Timeouts and AbortController
- WebSockets
- Service Workers and Progressive Web Apps (PWAs)
- Essential Browser APIs
- Introduction to WebAssembly
Module 8: Testing and Debugging
- Debugging JavaScript
- Code Quality: ESLint, Prettier and Conventions
- Unit Testing with Jest
- Test Doubles: Mocks, Stubs and Spies
- Integration Testing
- End-to-End Testing with Cypress
Module 9: Performance and Optimization
- Measure Before You Optimize: DevTools and Web Vitals
- Optimizing JavaScript Performance
- Memory Management
- Efficient DOM Manipulation
- Lazy Loading and Code Splitting
Module 10: JavaScript Frameworks and Libraries
- Why Frameworks Exist
- Introduction to React
- State Management with Redux
- Vue.js Basics
- Angular Basics
- Choosing the Right Framework
