---
title: Loops
source: 'https://academia.sh/en/courses/javascript-fundamentals/loops'
course: 'JavaScript Fundamentals'
language: en
updated: '2026-08-23T07:00:57+00:00'
license: 'CC BY-SA 4.0'
---

# Loops

Counted, conditional, and collection loops; the `for...in` versus `for...of` distinction, loop control, labeled exit, and traversing objects.

The previous lesson classified a single measurement. Processing the entire list
requires repetition. The Programming Fundamentals course's Loops and Loop Control
lessons established the purpose of these structures and the loop-invariant concept;
here, the forms JavaScript offers and the difference between them — especially between
the two collection loops — are covered.

There are four loop forms, and the choice among them is not arbitrary: the counted
loop is used when an index is needed, the conditional loop when the repeat count is not
known in advance, and the collection loops when working directly on elements.

## Counted and Conditional Loops

The classic `for` loop has three parts: initialization, condition, and end-of-round
action. As shown in the Variable Declarations lesson, the counter is declared with
`let` and rebound on every round.

```js
const temperatures = [21.5, 19.75, 23, 18.25];

for (let i = 0; i < temperatures.length; i++) {
  console.log(i, temperatures[i]);
}
```

```
0 21.5
1 19.75
2 23
3 18.25
```

The `while` loop carries only a condition; it is used when the repeat count is not
known in advance:

```js
const temperatures = [21.5, 19.75, 23, 18.25];
let i = 0;
while (i < temperatures.length && temperatures[i] < 23) {
  i += 1;
}
console.log("first index at or above 23:", i);
```

```
first index at or above 23: 2
```

The order in the condition matters. The bound check (`i < temperatures.length`) is
written first; thanks to short-circuit evaluation, the second comparison never runs
once the index is exceeded. Written in reverse order, the comparison
`undefined < 23` would run at the end of the array — this expression gives false
because it goes through a coercion that produces `NaN`, so the loop would still end,
but the reason would be coincidence rather than the guard.

The `do...while` form runs the body at least once; the condition is tested at the end
of the round:

```js
let round = 0;
do {
  round += 1;
} while (round < 3);
console.log("round:", round);
```

```
round: 3
```

## Two Collection Loops

JavaScript has two collection loops that resemble each other and **give different
things**.

`for...in` gives an object's **enumerable property keys**. `for...of` gives an
iterable value's **elements**.

```js
const temperatures = [21.5, 19.75, 23];

for (const index in temperatures) {
  console.log(typeof index, index);
}
for (const value of temperatures) {
  console.log(typeof value, value);
}
```

```
string 0
string 1
string 2
number 21.5
number 19.75
number 23
```

The first loop gave indices, and the indices are of **string type**. The reason is that
object keys are strings; an array is also an object and carries its indices as keys.
This explains why code that sums with `for...in` ends up concatenating text — the
addition rule from the Type Conversion and Coercion lesson kicks in.

The difference becomes even clearer once a property is added to the array:

```js
const temperatures = [21.5, 19.75, 23];
temperatures.source = "A1";

for (const index in temperatures) {
  console.log("for...in:", index);
}
for (const value of temperatures) {
  console.log("for...of:", value);
}
console.log("length:", temperatures.length);
```

```
for...in: 0
for...in: 1
for...in: 2
for...in: source
for...of: 21.5
for...of: 19.75
for...of: 23
length: 3
```

`for...in` also traversed a key that is not an array element; `for...of` gave only the
elements. The `length` value did not change either — the added property does not count
toward the array's length.

This gives the course's rule: **`for...of` is used on arrays.** `for...in` is written
only to traverse plain objects' keys.

For the case where both index and value are wanted together, there is a separate
method:

```js
const temperatures = [21.5, 19.75, 23];
for (const [index, value] of temperatures.entries()) {
  console.log(index, value);
}
```

```
0 21.5
1 19.75
2 23
```

Here the index is of number type. The bracketed spelling splits the pair's two
components into two names; this **destructuring** form is covered in detail in the
Arrays and Objects lessons.

## Traversing Objects

Plain objects cannot be traversed with `for...of`:

```js
const record = { station: "A1", temperature: 21.5, humidity: 48 };
try {
  for (const d of record) {
    console.log(d);
  }
} catch (error) {
  console.log(error.name + ": " + error.message);
}
```

```
TypeError: record is not iterable
```

The message is exact: the object is not **iterable**. Arrays, strings, and some
built-in collections are iterable; plain objects are not.

There are two paths:

```js
const record = { station: "A1", temperature: 21.5, humidity: 48 };

for (const key in record) {
  console.log(key, record[key]);
}
for (const [key, value] of Object.entries(record)) {
  console.log(key, "=", value);
}
```

```
station A1
temperature 21.5
humidity 48
station = A1
temperature = 21.5
humidity = 48
```

The two gave the same result, but their scope is not the same. `for...in` also
traverses inherited properties; `Object.entries` gives only the object's own
properties. The inheritance chain is a later course's subject; for now, it is enough to
know the second form is narrower and more predictable. In this course, object
traversal will be written with `Object.entries`.

## Loop Control

`break` ends a loop, `continue` skips that round. Both affect the innermost loop.

```js
const records = [
  { station: "A1", temperature: 21.5 },
  { station: "A2", temperature: NaN },
  { station: "B1", temperature: 23 },
  { station: "B2", temperature: 18.25 },
];

let total = 0;
let count = 0;
for (const record of records) {
  if (Number.isNaN(record.temperature)) {
    continue;
  }
  total += record.temperature;
  count += 1;
}
console.log("valid readings:", count, "average:", total / count);

let firstHigh = null;
for (const record of records) {
  if (record.temperature > 22) {
    firstHigh = record.station;
    break;
  }
}
console.log("first high:", firstHigh);
```

```
valid readings: 3 average: 20.916666666666668
first high: B1
```

The first loop skipped the invalid record and computed the average only over valid
readings — this is why `count` had to be used as the divisor instead of
`records.length`. The second loop stopped once it found what it was looking for; the
remaining records were never checked.

The average's digits are the result of the floating-point representation covered in the
Primitive Types lesson: the result of `62.75 / 3` cannot be written exactly as a binary
fraction and is rounded to the nearest representable value.

Breaking out of an outer loop from a nested one requires a **label**:

```js
const grid = [[1, 2], [3, 4], [5, 6]];
outerLoop: for (const row of grid) {
  for (const cell of row) {
    if (cell === 4) {
      console.log("found, breaking both loops");
      break outerLoop;
    }
    console.log("looking at:", cell);
  }
}
```

```
looking at: 1
looking at: 2
looking at: 3
found, breaking both loops
```

A label is a name placed in front of a loop; `break` and `continue` take this name to
say which loop they affect. An unlabeled `break` would have broken only the inner loop,
and the outer loop would have moved on to the next row.

Labeled exit is rarely needed. Wrapping the same work in a function and exiting with
`return` is usually more readable; function definition is the next lesson's subject.

## Summary

- Counted `for` is used when an index is needed, `while` when the repeat count is
  unknown, `do...while` when the body must run at least once.
- `for...in` gives property keys, and the keys are of string type; `for...of` gives
  elements.
- `for...of` is used on arrays; `for...in` also traverses non-array properties.
- Plain objects are not iterable; key–value traversal is written with
  `Object.entries`.
- `break` and `continue` affect the innermost loop; a label is used to reach an outer
  one.

## Next Step

In this lesson, checks and accumulators were embedded in the loop body; doing the same
work a second time would mean copying code. The next lesson moves to functions and
opens with a distinction specific to JavaScript: the difference between writing a
function as a declaration and as an expression is not just syntax — it also determines
when the name becomes accessible.
