Skip to content
JavaScript js control-flow 3 min read

for...of

The for...of loop, introduced in ES2015, walks the values of any iterable object — arrays, strings, Maps, Sets, DOM NodeLists, generators, and more. It reads like plain English, hands you each item directly, and frees you from manual index bookkeeping. For the common case of “do something with every element,” it is the cleanest loop JavaScript offers.

Basic syntax

You declare a variable that receives each value in turn, then name the iterable to loop over:

const colors = ["red", "green", "blue"];

for (const color of colors) {
  console.log(color);
}

Output:

red
green
blue

Use const for the loop variable unless you reassign it inside the body — each iteration creates a fresh binding, so closures capture the right value without the var pitfalls of an index loop.

Iterating different iterables

for...of works on anything that implements the iterable protocol, not just arrays. Strings yield their characters (correctly handling multi-byte code points):

for (const char of "café") {
  console.log(char);
}

Output:

c
a
f
é

A Set yields its unique values, and a Map yields [key, value] pairs you can destructure inline:

const visits = new Map([
  ["home", 120],
  ["about", 30],
]);

for (const [page, count] of visits) {
  console.log(`${page} → ${count}`);
}

Output:

home → 120
about → 30

In the browser, DOM collections such as the NodeList returned by document.querySelectorAll are iterable too, so you can loop them directly instead of converting to an array first:

for (const item of document.querySelectorAll("li")) {
  item.classList.add("seen");
}

Plain objects ({ a: 1 }) are not iterable and will throw TypeError: obj is not iterable. Loop their entries with for (const [k, v] of Object.entries(obj)) instead.

Getting the index with entries()

for...of gives you values, not positions. When you also need the index, call the array’s entries() method, which produces [index, value] pairs:

const fruits = ["apple", "banana", "cherry"];

for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

Output:

0: apple
1: banana
2: cherry

This keeps the readability of for...of while still exposing the index — no separate counter to declare or increment.

for…of vs for…in

These two look similar but iterate different things. for...of reads values from an iterable; for...in reads enumerable property keys (as strings) from any object, including inherited ones. Confusing them on arrays is a classic bug.

const arr = ["a", "b", "c"];
arr.extra = "oops";

for (const v of arr) console.log(v);   // values, skips non-index props
console.log("---");
for (const k in arr) console.log(k);   // keys, includes "extra"

Output:

a
b
c
---
0
1
2
extra
Featurefor...offor...in
IteratesValuesProperty keys (strings)
Works onIterables (arrays, strings, Map, Set…)Any object
Order guaranteedYes (iteration order)Mostly, but don’t rely on it
Includes inherited propsNoYes (enumerable ones)
Best forArrays and collectionsPlain object keys

The rule of thumb: use for...of for arrays and other collections, and for...in only for inspecting an object’s keys.

Control flow and async

for...of respects break, continue, and return normally — unlike forEach, which cannot be short-circuited:

for (const n of [1, 2, 3, 4, 5]) {
  if (n === 4) break;
  if (n % 2 === 0) continue;
  console.log(n);
}

Output:

1
3

It also pairs cleanly with await: inside an async function, the body can await each iteration in sequence, and the for await...of variant consumes async iterables like streamed data.

async function loadAll(urls) {
  for (const url of urls) {
    const res = await fetch(url);
    console.log(res.status);
  }
}

Best Practices

  • Reach for for...of as the default when you need each value and don’t need the index.
  • Use const for the loop variable unless the body reassigns it.
  • Call array.entries() (with destructuring) when you need both index and value.
  • Never use for...in to iterate array elements — it yields keys and inherited props.
  • Loop a Map or Set directly; destructure Map entries as [key, value].
  • Prefer for...of over forEach when you must break, continue, or await inside the loop.
Last updated June 1, 2026
Was this helpful?