Iterating arrays & array-likes
Choose the right loop for arrays, use keys, values and entries, convert array-likes with Array.from, and work safely with arguments and NodeList objects.
- 01Pick the right loopCompare
for,for...of, andforEachwhen skipping, stopping, and reading indexes. - 02Use iterator helpersReach for
keys(),values(), andentries()when you need positions, values, or both. - 03Handle array-likesConvert or borrow methods for
arguments, strings, plain array-like objects, and browserNodeLists.
Choose the right way to walk a list
Arrays are ordered lists, but not every “walk through the list” job is the same. Sometimes you need the index. Sometimes you need to stop as soon as you find a problem. Sometimes you want to do one side effect for every item and never produce a new array. This lesson helps you choose the right tool and recognize things that look array-ish but are not real arrays.
The curriculum summary says it neatly: choose the right loop, and turn array-likes into real arrays. We will connect what you already know from Loops: while & for, for…of & for…in, Default & rest parameters, and Symbols. The formal iteration protocols come later; here we use the pieces you can apply today.
Iterating an array means visiting its items in a predictable order. Array-like means “numbered slots plus a length,” but not necessarily array methods such as map or filter.
If you walk a row of lockers yourself, you can count by twos, walk backward, or stop at locker 12. A tour guide is less fiddly: they simply hand you each item in order, and you can still say “stop.” A delivery service has a route card and visits every valid house; you can skip a package, but you cannot end the whole route from inside one delivery.
- In real life: Walking the lockers yourself with a counter
- In JavaScript: A
forloop: full control over index, direction, skips, and stops - In real life: A tour guide hands you the next locker’s contents
- In JavaScript: A
for...ofloop: values in order, withbreakstill available - In real life: A delivery service visits every listed house
- In JavaScript:
forEach: a callback for each existing item, with no emergency stop button
Where the analogy stops: Real lockers are always physically present. JavaScript arrays can have holes: missing indexes that some tools visit as undefined and some tools skip.
Keep that mental model in mind as we compare the same task with three loops, then use iterator helpers, convert array-likes, borrow array methods carefully, and inspect real browser NodeLists.
for vs for…of vs forEach
STEP THROUGHOur task is deliberately small: sum scores until a negative number appears, skipping zeroes. The data is [3, 0, 4, -2, 5]. A correct “until negative” version returns 7, because the 5 after -2 should never be counted.
Same data, three loop styles. Predict whether the 5 after -2 is counted, then step through the real recorded run.
script
let total = 0;for (let index = 0; index < scores.length; index += 1) { const score = scores[index]; if (score === 0) continue; if (score < 0) break; total += score;}console.log(total);The lesson is not that one loop is always better. It is that each loop makes a different promise:
forgives you the counter. Use it when the index is the main story, you need to move in a custom pattern, or you need full control.for...ofgives you values. Use it when you want clear value-by-value code and may still needbreakorcontinue.forEachcalls a callback for each existing item. Use it for side effects you truly want to run for every item; do not use it when you need early exit.
| Tool | Can break? | Index by default? | Holes | Best for |
|---|---|---|---|---|
for | Yes | Yes | Visits every numeric index; reading a hole gives undefined | Full control: start, stop, skip, go backward, change step size |
for...of | Yes | No, unless you use entries() | Visits holes as undefined | Readable value-by-value loops that may stop early |
forEach | No | Second callback parameter | Skips holes | Do the same side effect for every existing item |
for...in | Yes | Gives property keys as strings | Skips holes but includes enumerable custom keys | Objects, not arrays |
Notice the fourth row: for...in is included because it is easy to confuse with for...of. On arrays it walks enumerable property keys as strings, including custom keys. Keep it for ordinary object properties; for arrays, choose one of the first three.
keys, values & entries
STEP THROUGHArrays have three small iterator helpers. keys() produces indexes, values() produces values, and entries() produces pairs: [index, value]. They do not produce arrays immediately; they produce iterator objects. If you want to see all results at once, convert them with spread syntax or Array.from.
Step through the three iterator helpers: keys for indexes, values for items, entries for both.
script
console.log([...queue.keys()].join(","));console.log([...queue.values()].join("|"));for (const [index, name] of queue.entries()) { console.log(index + ":" + name);}You already saw entries() with destructuring in the for…of lesson. The pattern is worth keeping:
const names = ["Ada", "Ben"];
for (const [index, name] of names.entries()) {
console.log(index, name);
}That loop reads like a sentence: “for each index-and-name pair of the names entries.” You avoid manually writing names[index], and your loop still supports break if a later condition needs it.
Sparse arrays are rare but important. Step through to see why for...of and forEach do not treat holes the same way.
script
for (const item of lockers) { console.log(item);}lockers.forEach((item) => { console.log(item);});Sparse arrays are uncommon in everyday UI code, but this edge case explains a lot of surprising output. for...of uses array iteration and visits the missing slot as undefined. Most array callback methods, including forEach, skip holes.
Array-likes & Array.from
INTERACTIVEAn array-like is an object with numbered slots and a length. It looks like a row of lockers, but it does not automatically have the array toolbelt. A plain object such as { 0: "a", 1: "b", length: 2 } is array-like; Array.isArray still says false.
Numbered slots tell you where to look, but they are not a real array. Array.from copies the visible slots into a real array, where its methods are available.
- In real life: Items written in numbered places
- In JavaScript: An array-like:
0,1, andlength - In real life: A list you can check and change
- In JavaScript: Array methods such as
map,filter,join, andslice - In real life: Rewrite the items on a real list
- In JavaScript:
Array.from(arrayLike)returning a real array
Where the analogy stops: Array.from copies values at that moment. If the original array-like changes later, the new array does not magically stay in sync.
An array-like can look like an array without owning array methods. Step through the conversion and the borrowed-method version.
script
console.log(Array.isArray(row));try { row.map((letter) => letter.toUpperCase());} catch (error) { console.log(error.name);}const real = Array.from(row);console.log(real.map((letter) => letter.toUpperCase()).join(""));console.log(Array.prototype.map.call(row, (letter) => letter + "!").join("|"));console.log(Array.prototype.join.call("abc", "-"));Array.from works with array-likes and iterables. Strings are both: they have indexed characters and a length, and they also work with for...of. A Set is iterable but not array-like, because it has no numeric slots and no array-style length.
["a", "b"]- the
argumentsobject inside a regular function "abc"{ 0: "a", 1: "b", length: 2 }- document.querySelectorAll(
section) - new Set([
a,b]) { name: "Ada" }
Sort each value by its shape. Ask: is it a real Array? If not, does it have numbered slots and a length?
Borrowing array methods for array-likes
PRACTICALSometimes you do not need to keep the converted array. You only need one array method to read the indexed slots. JavaScript lets you borrow a method from Array.prototype and run it with a different receiver: Array.prototype.map.call(row, fn). In one sentence, call invokes a function while choosing what this means inside it; the later call, apply & bind lesson goes deeper.
const row = { 0: "a", 1: "b", length: 2 };
const excited = Array.prototype.map.call(row, (letter) => letter + "!");
console.log(excited.join("|"));
console.log(Array.prototype.join.call("abc", "-"));Borrowing is useful in tiny adapters and older code, but conversion is usually clearer for learners and teams: Array.from(row).map(fn). Use borrowing when you specifically want “run this array method once on this array-like value.” Use conversion when you will keep using array methods or pass the result around.
If a teammate has to pause at Array.prototype.map.call, Array.from(row).map may be the better choice. The tiny performance difference rarely matters in beginner-to-intermediate application code.
arguments & NodeList
LIVE BROWSERTwo array-likes show up in real code: arguments inside a regular function and NodeList from browser DOM methods such as querySelectorAll. You met arguments in Default & rest parameters. Today, prefer rest parameters when you control the function; they create a real array immediately.
function list() {
console.log(Array.isArray(arguments));
console.log([...arguments].join("|"));
console.log(Array.from(arguments).map((value) => value * 2).join(","));
}
list(2, 4, 6);In modern engines, spreading arguments works, so [...arguments] makes an array. Still, Array.isArray(arguments) is false, and arguments.map is missing. Rest parameters are cleaner: function list(...values) { ... } gives you a real values array.
A NodeList appears when browser code asks the DOM for a group of nodes. The DOM itself is taught later in Walking & searching the DOM, but the shape matters now: querySelectorAll returns a NodeList, not an Array.
const sections = document.querySelectorAll("section");
console.log(sections.length);
console.log(typeof sections.forEach);
console.log(typeof sections.map);
const headings = Array.from(sections, (section) => section.id);
console.log(headings.join(", "));const sections = document.querySelectorAll("section");console.log(sections.length);console.log(typeof sections.forEach);console.log(typeof sections.map);const headings = Array.from(sections, (section) => section.id);console.log(headings.join(", "));- sections.length
- 0
- typeof sections.forEach
- not checked yet
- typeof sections.map
- not checked yet
- Array.from ids
- Run the check to read this page.
Click the button to ask the browser for every section element in this article.
length, usually forEach, and no map.In most modern browsers, NodeList has forEach but not map. That is why production code often says Array.from(document.querySelectorAll("button")) before mapping, filtering, or sorting nodes.
Where you will use this
Iteration choices show up anywhere you process UI data. Use for...of to validate fields until the first error, because you can break or return from the surrounding function. Use entries() when an error message needs “item 3” as well as the value. Use Array.from when browser APIs hand you an array-like collection.
const fields = ["name", "email", "password"];
const values = ["Ada", "", "secret"];
for (const [index, value] of values.entries()) {
if (value === "") {
console.log(fields[index] + " is required");
break;
}
}This is the comfortable middle ground: readable values, an index when you need it, and early exit when continuing would be noisy.
Common misconceptions
- “return stops forEach.” It stops only the current callback call. The outer
forEachkeeps scheduling the remaining items. - “Array-like means Array.” Array-like means numbered slots plus
length.Array.isArraycan still befalse. - “NodeList has all array methods because it has forEach.” In most browsers it has
forEach, but methods such asmap,filter, andreducelive on real arrays. - “for...in is the index loop for arrays.” It is a property-key loop. It can see non-index enumerable properties and returns keys as strings.
- “Holes behave like explicit undefined values everywhere.”
for...ofvisits holes asundefined, whileforEachskips them. - “Borrowing is always better than converting.” Borrowing is compact but advanced.
Array.fromis often clearer, especially before several array operations.
Practice exercises
5 EXERCISESforEach with return doesRead the code and type the exact number printed.
const scores = [3, 0, 4, -2, 5];
let total = 0;
scores.forEach((score) => {
if (score === 0) return;
if (score < 0) return;
total += score;
});
console.log(total);The program prints 12: it adds 3, skips 0, adds 4, returns for -2, then still adds 5.
Run or reason through the starter. It converts the row, maps each letter to uppercase, and prints the joined result.
const row = { 0: "x", 1: "y", length: 2 };
const result = Array.from(row).map((letter) => letter.toUpperCase());
console.log(result.join(""));const row = { 0: "x", 1: "y", length: 2 };
const result = Array.from(row).map((letter) => letter.toUpperCase());
console.log(result.join(""));Array.from(row) creates ["x", "y"]. Mapping to uppercase and joining prints XY.
entries()What is the second line printed by this loop?
const pets = ["cat", "dog"];
for (const [index, pet] of pets.entries()) {
console.log(index + ":" + pet);
}const pets = ["cat", "dog"];
for (const [index, pet] of pets.entries()) {
console.log(index + ":" + pet);
}The loop prints 0:cat and then 1:dog. Destructuring names the pair as index and pet.
In the browser console, run the NodeList snippet from this lesson. Then answer which array method is missing directly on the NodeList.
const sections = document.querySelectorAll("section");
console.log(sections.length);
console.log(typeof sections.forEach);
console.log(typeof sections.map);
const headings = Array.from(sections, (section) => section.id);
console.log(headings.join(", "));A NodeList from querySelectorAll has length and usually forEach, but map is missing. Convert it with Array.from(sections) first.
join from ArrayPredict the output of the borrowed method call.
console.log(Array.prototype.join.call("abc", "-"));console.log(Array.prototype.join.call("abc", "-"));Array.prototype.join.call("abc", "-") treats the string as array-like and prints a-b-c.
Check your understanding
8 QUESTIONSQuestion 1 of 8Which loop should you choose when you may need to stop as soon as a negative number appears?
Choose an answer to see the explanation.
Question 2 of 8What does the
for...oftotal program print?Read the code, then predictconst values = [1, 0, 2, -1, 9]; let total = 0; for (const value of values) { if (value === 0) continue; if (value < 0) break; total += value; } console.log(total);Choose an answer to see the explanation.
Question 3 of 8How many callback calls does this sparse-array
forEachcount?Read the code, then predictconst sparse = ["a", , "b"]; let count = 0; sparse.forEach(() => { count += 1; }); console.log(count);Choose an answer to see the explanation.
Question 4 of 8Which expression gives
[0, "Ada"]as the first pair forconst names = ["Ada"]?Choose an answer to see the explanation.
Question 5 of 8What does
Array.from(row).join("-")print?Read the code, then predictconst row = { 0: "a", 1: "b", length: 2 }; console.log(Array.from(row).join("-"));Choose an answer to see the explanation.
Question 6 of 8What does borrowed
joinprint for the string"abc"?Read the code, then predictconsole.log(Array.prototype.join.call("abc", "-"));Choose an answer to see the explanation.
Question 7 of 8Inside a regular function in modern JavaScript, what is true about
arguments?Choose an answer to see the explanation.
Question 8 of 8A browser
NodeListfromquerySelectorAllusually has which pair of features?Choose an answer to see the explanation.
Key takeaways
- Use
forwhen index control matters most. - Use
for...offor readable value loops that can stillbreakorcontinue. - Use
forEachfor side effects on every existing item, not for early exit or producing a new array. keys(),values(), andentries()return iterators; spread orArray.fromreveals all their values.- Array-likes have numbered slots and
length. Convert withArray.fromwhen you want the full array toolbelt.
Iteration is choosing the right way to visit items; array-like handling is choosing whether to convert first or borrow one method carefully.
Up next: Destructuring arrays & objects.