Ad – 728Γ—90
πŸ”€ Control Flow

JavaScript For Loop – Iterating with for, for...of, and for...in

JavaScript provides three distinct for-loop constructs: the classic indexed loop, the modern for...of for iterables, and for...in for object keys. Learn all three, understand their differences, and know exactly when to use each.

⏱️ 20 min read 🎯 Beginner πŸ“… Updated 2026

The Classic for Loop

The traditional for loop gives you full control with three parts: initializer, condition, and update expression.

JavaScript
// Syntax: for (init; condition; update) { body }
for (let i = 0; i < 5; i++) {
  console.log("Iteration " + i);
}

// Count down
for (let i = 10; i >= 0; i -= 2) {
  console.log(i);
}

// Iterate an array by index
const colors = ["red", "green", "blue"];
for (let i = 0; i < colors.length; i++) {
  console.log(i + ": " + colors[i]);
}
β–Ά Output
Iteration 0 Iteration 1 Iteration 2 Iteration 3 Iteration 4 10 8 6 4 2 0 0: red 1: green 2: blue
πŸ’‘
Cache the Length

For large arrays, cache arr.length in a variable to avoid re-evaluating it each iteration: for (let i = 0, len = arr.length; i < len; i++). Modern engines optimize this anyway, but it is good practice.

for...of Loop

Introduced in ES6, for...of iterates over any iterable object β€” arrays, strings, Sets, Maps, NodeLists, generators, and more. It gives you the value directly, without an index.

JavaScript
// Array
const fruits = ["apple", "banana", "cherry"];
for (const fruit of fruits) {
  console.log(fruit);
}

// String β€” iterates characters
for (const char of "Hello") {
  process.stdout.write(char + " ");
}
console.log();

// Set β€” only unique values
const unique = new Set([1, 2, 2, 3, 3]);
for (const val of unique) {
  console.log(val);
}

// Map β€” [key, value] pairs via destructuring
const scores = new Map([["Alice", 95], ["Bob", 87]]);
for (const [name, score] of scores) {
  console.log(name + ": " + score);
}
β–Ά Output
apple banana cherry H e l l o 1 2 3 Alice: 95 Bob: 87

for...in Loop

for...in iterates over the enumerable property keys of an object. It is designed for plain objects, not arrays.

JavaScript
const person = { name: "Alice", age: 30, city: "London" };

for (const key in person) {
  console.log(key + ": " + person[key]);
}
β–Ά Output
name: Alice age: 30 city: London
⚠️
Prototype Chain Warning

for...in also enumerates inherited properties. Use hasOwnProperty() or Object.hasOwn() to check that a key belongs directly to the object, not its prototype chain.

JavaScript
function Animal(name) {
  this.name = name;
}
Animal.prototype.type = "animal"; // Inherited property

const dog = new Animal("Rex");

// Without guard β€” includes inherited key
for (const key in dog) {
  console.log(key); // name, type
}

// With guard β€” own properties only
for (const key in dog) {
  if (Object.hasOwn(dog, key)) {
    console.log(key); // name only
  }
}

for...of vs for...in β€” Comparison

Featurefor...offor...in
Iterates overValues of an iterableEnumerable keys of an object
Works on arraysYes (preferred)Yes (but gives indices as strings)
Works on objectsNo (objects aren't iterable by default)Yes
Works on stringsYes (characters)Yes (indices as strings)
Includes inheritedNoYes β€” use hasOwnProperty guard
ES versionES6+ES1 (original)

Nested For Loops

A loop inside another loop β€” common for processing 2D arrays or generating combinations.

JavaScript
// Multiplication table (3x3)
for (let i = 1; i <= 3; i++) {
  let row = "";
  for (let j = 1; j <= 3; j++) {
    row += (i * j).toString().padStart(4);
  }
  console.log(row);
}

// Traverse a 2D array
const grid = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

for (const row of grid) {
  for (const cell of row) {
    process.stdout.write(cell + " ");
  }
  console.log();
}
β–Ά Output
1 2 3 2 4 6 3 6 9 1 2 3 4 5 6 7 8 9

Loop Variable Scope: let vs var

Using var in a for loop creates a function-scoped variable, which can cause bugs when the loop variable is captured in a closure.

JavaScript
// Bug with var β€” all closures share the same i
const funcsVar = [];
for (var i = 0; i < 3; i++) {
  funcsVar.push(function() { return i; });
}
console.log(funcsVar[0]()); // 3 (not 0!)
console.log(funcsVar[1]()); // 3
console.log(funcsVar[2]()); // 3

// Fixed with let β€” each iteration gets its own i
const funcsLet = [];
for (let j = 0; j < 3; j++) {
  funcsLet.push(function() { return j; });
}
console.log(funcsLet[0]()); // 0
console.log(funcsLet[1]()); // 1
console.log(funcsLet[2]()); // 2
β–Ά Output
3 3 3 0 1 2

When to Use forEach Instead

Array.prototype.forEach is a functional alternative when you just want to do something with each element and don't need break, continue, or await.

JavaScript
const nums = [1, 2, 3, 4, 5];

// forEach β€” clean, but cannot break out
nums.forEach((n, index) => {
  console.log(index + ": " + n);
});

// Use for...of when you need to break
for (const n of nums) {
  if (n === 3) break; // forEach can't do this
  console.log(n);
}
β–Ά Output
0: 1 1: 2 2: 3 3: 4 4: 5 1 2
Ad – 336Γ—280

πŸ‹οΈ Practical Exercise

Given the array const students = [{name:"Alice",grade:88},{name:"Bob",grade:72},{name:"Carol",grade:95}]:

  1. Use a classic for loop to print each student's name and grade.
  2. Use for...of with destructuring to compute the average grade.
  3. Use for...in on the first student object to list all its keys.

πŸ”₯ Challenge Exercise

Write a function flattenMatrix(matrix) that uses nested for loops to convert a 2D array into a 1D array. Then write a second version using for...of with destructuring. Test with a 3Γ—4 matrix of your choice.

πŸ“‹ Summary

  • Classic for (init; cond; update) β€” precise control, index access.
  • for...of β€” cleanest for iterating values of arrays, strings, Sets, Maps.
  • for...in β€” for object keys; use Object.hasOwn() to skip inherited keys.
  • Always use let (not var) for loop counters to avoid closure bugs.
  • forEach is clean but can't use break, continue, or await.

Interview Questions

  • What is the difference between for...of and for...in?
  • Why does using var in a for loop cause closure bugs? How does let fix it?
  • Can you use for...of on a plain object? Why or why not?
  • What are the limitations of forEach compared to a for loop?
  • How do you iterate over the entries (key-value pairs) of an object?

Frequently Asked Questions

Can I use for...of on a DOM NodeList?+

Yes, NodeList is iterable since modern browsers implement the iterable protocol. You can write for (const el of document.querySelectorAll('p')) { ... }. For older browsers, convert first: Array.from(nodeList).

How do I get the index when using for...of?+

Use Array.entries(): for (const [index, value] of arr.entries()) { ... }. This gives you both the index and the value with clean destructuring syntax.

Is for...of slower than a classic for loop?+

In micro-benchmarks, a classic for loop can be marginally faster. However, the difference is negligible for most applications. Prefer for...of for readability; switch to a classic loop only if you have measured a real performance bottleneck.