Walking an array once required a counter, a condition and an increment, which is three chances to make a mistake on a trivial operation. The for...of loop removed that ceremony: it hands you the values directly, one at a time.
The word of is used almost nowhere else in the language. It separates the variable receiving each value from the collection being walked.
Definition
of is the keyword of the for...of loop, which walks the values of an iterable collection and runs its block once per value.
const items = ['tea', 'coffee', 'sugar'];
for (const item of items) {
console.log(item);
}
// 'tea', then 'coffee', then 'sugar'The variable is declared with const in the vast majority of cases: it is recreated on every pass, and nothing inside the block needs to change it.
for...of or for...in
The two loops differ by one word and do entirely different things. This is the most common confusion in the language on this subject.
| Loop | What it gives you | On an array |
|---|---|---|
for...of | The values | 'tea', 'coffee' |
for...in | The property names | '0', '1', as text |
On an array, for...in returns indexes as strings, and throws inherited properties into the mix as well. Keep the simple rule: of for the values of a collection, in for the keys of an object.
What can be walked, and what cannot
The loop accepts anything the language considers iterable: an Array, a string, a Map, a Set, the results of a selection in a page.
for (const letter of 'tea') { } // 't', 'e', 'a'
for (const value of new Set([1, 1, 2])) { } // 1, 2
for (const [key, value] of new Map()) { } // pairsA plain object, on the other hand, is not iterable: the loop raises an error. The way through is Object.entries(), paired with Destructuring to pick up the key and the value.
const stock = { tea: 12, coffee: 3 };
for (const [product, quantity] of Object.entries(stock)) {
console.log(product, quantity);
}What forEach cannot do
The Array.forEach() method is shorter, but it cannot be interrupted. A break or a continue has no effect inside it, and an await within is never waited for.
for (const id of ids) {
const record = await load(id);
if (!record) break;
}That is the most reliable way to choose: as soon as you need to stop partway or wait for something on each pass, for...of is the answer. For a simple transformation, Map or Array.filter() stay more expressive.
Frequently asked questions
How do I get the index along with the value?
An array's entries() method returns pairs, which unpack straight into the loop: for (const [i, value] of items.entries()). It reads better than keeping a separate counter, and the index always stays in step with the value.
Why does my object raise an error?
Because a plain object does not know how to be walked: the message says it is not iterable. Go through Object.keys(), Object.values() or Object.entries() depending on what you need, since all three return arrays.
Does the word of appear anywhere else in the language?
You meet it again in for await...of, used to walk values that arrive over time, and in the Array.of() method, which builds an array from the values it receives. Note that Array.of(7) gives a one-element array, where Array(7) creates one with seven empty slots.