Q33 of 40 · JavaScript

What is the difference between `for...of`, `for...in`, and `forEach()`, and when should each be used?

JavaScriptSeniorjavascriptiterationfor-offor-inforEachiterators

Short answer

Short answer: `for...of` iterates over iterable values (arrays, strings, Maps, Sets, generators) using the iterator protocol. `for...in` iterates over enumerable string-keyed properties including inherited ones — generally avoid on arrays. `forEach` is an Array method for side effects with no early-exit mechanism.

Detail

These three constructs look similar but operate at very different levels of the language.

for...of: Uses the Symbol.iterator protocol. Works on any iterable: arrays, strings, Maps, Sets, NodeLists, generators, and custom objects that implement Symbol.iterator. Supports break, continue, return, and await inside for await...of. This is the modern default for iterating values.

for...in: Iterates over all enumerable string-keyed properties of an object, including inherited properties from the prototype chain. On arrays, it iterates indices as strings plus any enumerable prototype properties — this is why using for...in on arrays is risky. Designed for plain object property enumeration.

forEach(): An Array (and Map/Set) prototype method. Synchronous. Always iterates every element — you cannot break early with break (throw an exception to escape, which is ugly). Cannot be used with await in a useful way — use for...of for async iteration.

In test automation: for...of with await is the correct pattern for sequential async iteration over test fixtures. for...in should be avoided on arrays; forEach is fine for synchronous side effects with no early exit.

// EXAMPLE

const arr = [10, 20, 30];

// for...of — iterates values, supports break/await
for (const val of arr) {
  if (val === 20) break; // works!
  console.log(val); // 10
}

// for...in — iterates keys (as strings), includes prototype!
Array.prototype.myMethod = () => {};
for (const key in arr) {
  console.log(key); // "0", "1", "2", "myMethod" ← dangerous!
}

// forEach — no break, no await
arr.forEach(val => {
  // break; // SyntaxError — break not allowed here
  console.log(val);
});

// Async sequential iteration — for...of + await
for (const user of users) {
  await db.insert(user); // sequential — each awaited
}

// Map iteration with for...of
const map = new Map([["a", 1], ["b", 2]]);
for (const [key, value] of map) { /* key-value pairs */ }

// WHAT INTERVIEWERS LOOK FOR

Clear distinction between all three. Knowing `for...in` is dangerous on arrays due to prototype enumeration. Understanding that `forEach` has no `break` or `await` support. `for await...of` for async sequential iteration shows senior-level async fluency.

// COMMON PITFALL

Using `for...in` on arrays — it iterates string indices and any enumerable prototype properties, not just array values. Extending Array.prototype in test helpers (a bad practice) makes `for...in` on arrays especially unpredictable.