SECTION 1
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.
1995 • JavaScript Created
for loop borrowed from C and Java, using an index variable to count through arrays.1999 • ECMAScript 3 & for...in
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.2009 • ES5 Adds forEach
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.2015 • ES6 Introduces for...of
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.
SECTION 2
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.KEY TAKEAWAY
SECTION 3
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.
SECTION 4
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:
INDEX LOOP SYNTAX
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
FOR...OF SYNTAX
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.
Getting the Index with for...of
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.SECTION 5
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 |
Common loop patterns and their ideal use cases
SECTION 6
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.
Solution A — Index-Based for Loop
- 1
Step 1 — Set Up the Data
We start by declaring the array and initializing two tracking variables:let highScore = scores[0];andlet bestQuiz = 0;. We assume the first element is the highest until proven otherwise.const scores = [78, 92, 85, 96, 88]; - 2
Step 2 — Write the Loop
We startiat 1 (since we already used index 0). On each iteration we comparescores[i]tohighScore. If the current score is larger, we update bothhighScoreandbestQuiz.for (let i = 1; i < scores.length; i++) { if (scores[i] > highScore) { highScore = scores[i]; bestQuiz = i; } } - 3
Step 3 — Trace the Execution
i=1: 92 > 78 → update to 92, bestQuiz=1. i=2: 85 > 92? No. i=3: 96 > 92 → update to 96, bestQuiz=3. i=4: 88 > 96? No. Loop ends.highScore = 96, bestQuiz = 3
Solution B — for...of with entries()
- 1
Step 1 — Same Data, Different Loop
We usefor (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; } } - 2
Step 2 — Compare the Two Approaches
Both produce the exact same result. Thefor...ofversion avoids thescores[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.Same result: highScore = 96 at quiz 3
SECTION 7
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) |
Feature comparison between the two loop styles
KEY TAKEAWAY
SECTION 8
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.) |
How basic loops connect to advanced array methods
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.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
for...of loop and an index-based for loop. When would you prefer one over the other?Show answer
for loop uses a counter variable to track position and accesses elements by their index (e.g., arr[i]). A for...of loop directly gives you each value without any counter. You would prefer for...of when you just need to process every value — it is shorter and eliminates off-by-one errors. Choose the index-based loop when you need the index for calculations, want to iterate backward, or need a custom step size like visiting every other element.Question 2: BASIC CALCULATION
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?Show answer
let sum = 0; before the loop. Then write for (const t of temps) { sum += t; }. After the loop, console.log(sum); will output 372 (since 72 + 68 + 75 + 80 + 77 = 372). The for...of loop is ideal here because we only need the values and do not require any index.Question 3: INTERMEDIATE
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?Show answer
const longWords = [];. Then write: for (let i = 0; i < words.length; i++) { if (words[i].length > 3) { longWords.push(words[i]); } }. The result is ["hello", "world", "javascript"]. The words "foo" (3 chars) and "bar" (3 chars) fail the > 3 test. Note that a for...of loop would work equally well here since no index is used in the logic.Question 4: APPLIED
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.Show answer
for (const [i, grade] of grades.entries()) { if (grade >= 60) continue; console.log(`Student ${i} scored ${grade}`); }. This outputs "Student 1 scored 45" and "Student 5 scored 55". The continue keyword skips the rest of the loop body when the grade is 60 or above, so only failing grades get printed. Using .entries() gives us access to the student number (index) alongside the grade value, combining the benefits of both loop styles.Question 5: CRITICAL THINKING
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.Show answer
i <= data.length — it should be i < data.length. Since data.length is 5, the loop runs with i = 5, and data[5] is undefined. Multiplying undefined * 2 produces NaN, so the output is 20, 40, 60, 80, 100, NaN. Fix (a): change <= to <. Fix (b): for (const num of data) { console.log(num * 2); } — this completely avoids the off-by-one risk since JavaScript handles the bounds automatically. This is a great example of why for...of is often preferred for simple forward iteration.SUMMARY
Lesson Summary
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().