Historical Context & Motivation
From the earliest days of programming, developers have needed a way to repeat actions across collections of data. Imagine you have a list of 1,000 student names and you need to print each one. Writing 1,000 separate console.log() statements would be absurd. Loops solve this problem by letting you write one block of code that automatically repeats for every item in a collection.
JavaScript has evolved significantly over its three decades. Early versions offered only the classic for loop with an index counter, which gave programmers precise control but also invited common mistakes. As the language matured, new loop styles were introduced to make iteration safer and more readable.
for loop borrowed from C and Java, using an index variable to count through arrays.for...in, designed to iterate over object properties. Developers sometimes misuse it on arrays, leading to subtle bugs because it iterates over keys (including inherited ones), not values.Array.prototype.forEach() method arrives, providing a callback-based approach. It is cleaner than index loops for simple tasks but cannot be exited early with break.for...of loop, designed specifically for iterating over iterable objects like arrays, strings, Maps, and Sets. It gives direct access to values with full support for break and continue.Today's JavaScript developer has multiple tools for iterating arrays. The central question this lesson addresses is: when should you use a for...of loop versus a traditional index-based for loop, and how do you write each one correctly? By the end of this lesson, you will be able to choose the right loop for any situation.
Core Principles & Definitions
Before diving into syntax, it helps to understand the foundational ideas behind array iteration. An array is an ordered collection of values, where each value sits at a numbered position called an index. The first element lives at index 0, the second at index 1, and so on. Iteration means visiting each element in order and performing some action on it.
Index-Based for Loop
i) that starts at 0 and increments until it reaches array.length. You access elements via array[i]. Best when you need the index or want to skip/reverse elements.for...of Loop
Iteration vs. Indexing
for...of focuses on iteration alone.Loop Control: break & continue
break (exit the loop immediately) and continue (skip the current iteration and move to the next). This gives you full control over the looping process.Visual Explanation
The diagram below shows how each loop style processes the same array. On the left, the index-based for loop tracks a counter variable i that advances from 0 to 3. On the right, the for...of loop directly receives each value without managing an index. Both visit every element, but their internal mechanics differ.
for loop tracks a counter variable i and accesses elements by position. Right: the for...of loop hands you each value directly, hiding the index mechanics.Notice how the left panel requires you to initialize i, test i < arr.length, and increment i++ — three separate pieces you must get right. The right panel eliminates all of that. JavaScript's iteration protocol handles advancement and termination behind the scenes, so you just write your logic inside the loop body.
How Each Loop Works Under the Hood
The Index-Based for Loop
The classic for loop has three parts inside its parentheses, separated by semicolons: initialization, condition, and update. Here is the general pattern:
let i = 0 — initialization: create counter at 0. i < array.length — condition: keep going while true. i++ — update: add 1 to the counter after each pass.The engine runs the initialization once, then repeats a cycle: check the condition → if true, execute the body → run the update → repeat. The moment the condition evaluates to false, the loop exits. Because arrays are zero-indexed, you typically use i < array.length (strict less-than) rather than <=. Using <= would try to access one element past the end of the array, resulting in undefined — a classic off-by-one error.
The for...of Loop
const element — a new variable created on each iteration holding the current value. of array — tells JavaScript which iterable to traverse.Behind the scenes, for...of uses the array's built-in iterator protocol. JavaScript calls a hidden method (Symbol.iterator) to get an iterator object, then repeatedly calls .next() on it. Each call returns { value, done }. When done is true, the loop stops. You do not need to manage any of this yourself — JavaScript handles it automatically.
for...of with array.entries(): for (const [i, val] of arr.entries()) { ... }. The .entries() method returns pairs of [index, value], and destructuring unpacks each pair into two variables.Syntax Patterns & Variations
Both loop styles have useful variations beyond the basic pattern. Understanding these will help you handle real-world situations like iterating backward, skipping elements, or exiting early.
for...of; switch to an index-based loop when you need to traverse backward or skip elements by a custom step.Common Variations
| Pattern | Code | When to Use |
|---|---|---|
| Forward (standard) | for (let i = 0; i < arr.length; i++) | Default index-based pattern; most common |
| Backward | for (let i = arr.length - 1; i >= 0; i--) | Reversing output, removing items while iterating |
| Skip by 2 | for (let i = 0; i < arr.length; i += 2) | Processing every other element (e.g., even indices) |
| for...of (values) | for (const val of arr) | Simple iteration, no index needed |
| for...of + entries | for (const [i, val] of arr.entries()) | Need both index and value with cleaner syntax |
Worked Example — Finding the Highest Score
Suppose you have an array of quiz scores and you need to find the highest score along with which quiz number it belongs to. We will solve this with both loop styles so you can see the difference in practice.
let highScore = scores[0]; and let bestQuiz = 0;. We assume the first element is the highest until proven otherwise.const scores = [78, 92, 85, 96, 88];i at 1 (since we already used index 0). On each iteration we compare scores[i] to highScore. If the current score is larger, we update both highScore and bestQuiz.for (let i = 1; i < scores.length; i++) { if (scores[i] > highScore) { highScore = scores[i]; bestQuiz = i; } }for (const [i, score] of scores.entries()) to get both the index and the value in a single, readable line. The destructuring [i, score] automatically splits each pair.for (const [i, score] of scores.entries()) { if (score > highScore) { highScore = score; bestQuiz = i; } }for...of version avoids the scores[i] bracket syntax and eliminates the manual increment. However, the index-based version lets us start at index 1 directly, which is slightly more efficient since it skips one comparison.Strengths & Limitations
Neither loop style is universally better. Each shines in specific situations. The table below breaks down the key differences to help you make an informed choice every time you write a loop.
| Feature | Index-Based for | for...of |
|---|---|---|
| Readability | More verbose — three-part header | Cleaner — reads almost like English |
| Index access | Built in — just use i | Requires .entries() |
| Backward iteration | Easy — just change init and update | Not directly supported |
| Custom step size | Easy — i += 2, etc. | Not supported |
| break / continue | Fully supported | Fully supported |
| Off-by-one errors | Common risk | Eliminated |
| Works with non-arrays | Only array-like objects with .length | Any iterable (strings, Maps, Sets) |
Connection to Advanced Iteration
The loops you have learned in this lesson form the foundation for more advanced iteration patterns you will encounter as you grow as a developer. JavaScript's array methods like .map(), .filter(), and .reduce() build on the same concept of visiting each element but wrap the loop in a function call. Understanding the raw loops first makes those higher-order methods much easier to grasp.
| Concept You Know Now | Advanced Version | Key Difference |
|---|---|---|
for...of over arrays | for...of over generators | Generators produce values lazily, on demand, instead of storing them all in memory |
Index-based loop with break | .find() / .findIndex() | Returns the first matching element or index without manual break logic |
| Loop that builds a new array | .map() | Returns a transformed copy of the array in a single expression |
| Loop that filters elements | .filter() | Returns a new array with only the elements that pass a test function |
| Loop that accumulates a total | .reduce() | Combines all elements into a single result (sum, object, etc.) |
Another important concept is asynchronous iteration. In future courses, you may encounter for await...of, which works like for...of but handles data that arrives over time (like streaming data from a server). Mastering synchronous loops now prepares you to understand that async pattern later.
Practice Problems
for...of loop and an index-based for loop. When would you prefer one over the other?const temps = [72, 68, 75, 80, 77];, write a for...of loop that calculates and logs the sum of all temperatures. What will the sum be?const words = ["hello", "world", "foo", "bar", "javascript"];, use an index-based for loop to build a new array containing only the words that have more than 3 characters. Store the result in a variable called longWords. What does longWords contain?const grades = [88, 45, 92, 73, 61, 55, 97];. Using for...of with .entries(), write code that logs the index and grade of every student who scored below 60. Use continue to skip students who passed.const data = [10, 20, 30, 40, 50]; for (let i = 0; i <= data.length; i++) { console.log(data[i] * 2); } There is a bug. Identify it, explain what output this produces, and fix the code using (a) an index-based loop and (b) a for...of loop.Lesson Summary
JavaScript provides two primary ways to iterate arrays. The index-based for loop uses three parts — initialization, condition, and update — to manually control a counter variable. It excels when you need the index, want to iterate backward, or need a custom step size. However, it carries the risk of off-by-one errors if the condition uses <= instead of <.
The for...of loop, introduced in ES6 (2015), iterates directly over values using the iterator protocol. It is cleaner, safer, and works on any iterable — arrays, strings, Maps, and Sets. When you need both the index and the value, combine it with .entries(). Both loops support break and continue for full control over the iteration process. Mastering these patterns prepares you for higher-order array methods like .map(), .filter(), and .reduce().