How can I loop through all the entries in an array using JavaScript?
I thought it was something like this:
forEach(instance in theArray)
Where theArray is my array, but this seems to be incorrect.
arraysforeachiterationjavascriptloops
How can I loop through all the entries in an array using JavaScript?
I thought it was something like this:
forEach(instance in theArray)
Where theArray is my array, but this seems to be incorrect.
Best Solution
TL;DR
Your best bets are usually
for-ofloop (ES2015+ only; spec | MDN) - simple andasync-friendlyforEach(ES5+ only; spec | MDN) (or its relativessomeand such) - notasync-friendly (but see details)forloop -async-friendlyfor-inwith safeguards -async-friendlySome quick "don't"s:
for-inunless you use it with safeguards or are at least aware of why it might bite you.mapif you're not using its return value.(There's sadly someone out there teaching
map[spec / MDN] as though it wereforEach— but as I write on my blog, that's not what it's for. If you aren't using the array it creates, don't usemap.)forEachif the callback does asynchronous work and you want theforEachto wait until that work is done (because it won't).But there's lots more to explore, read on...
JavaScript has powerful semantics for looping through arrays and array-like objects. I've split the answer into two parts: Options for genuine arrays, and options for things that are just array-like, such as the
argumentsobject, other iterable objects (ES2015+), DOM collections, and so on.Okay, let's look at our options:
For Actual Arrays
You have five options (two supported basically forever, another added by ECMAScript 5 ["ES5"], and two more added in ECMAScript 2015 ("ES2015", aka "ES6"):
for-of(use an iterator implicitly) (ES2015+)forEachand related (ES5+)forloopfor-incorrectly(You can see those old specs here: ES5, ES2015, but both have been superceded; the current editor's draft is always here.)
Details:
1. Use
for-of(use an iterator implicitly) (ES2015+)ES2015 added iterators and iterables to JavaScript. Arrays are iterable (so are strings,
Maps, andSets, as well as DOM collections and lists, as you'll see later). Iterable objects provide iterators for their values. The newfor-ofstatement loops through the values returned by an iterator:It doesn't get simpler than that! Under the covers, that gets an iterator from the array and loops through the values the iterator returns. The iterator provided by arrays provides the values of the array elements, in order beginning to end.
Notice how
elementis scoped to each loop iteration; trying to useelementafter the end of the loop would fail because it doesn't exist outside the loop body.In theory, a
for-ofloop involves several function calls (one to get the iterator, then one to get each value from it). Even when that's true, it's nothing to worry about, function calls are very cheap in modern JavaScript engines (it bothered me forforEach[below] until I looked into it; details). But additionally, JavaScript engines optimize those calls away (in performance-critical code) when dealing with native iterators for things like arrays.for-ofis entirelyasync-friendly. If you need the work in a loop body to be done in series (not in parallel), anawaitin the loop body will wait for the promise to settle before continuing. Here's a silly example:Note how the words appear with a delay before each one.
It's a matter of coding style, but
for-ofis the first thing I reach for when looping through anything iterable.2. Use
forEachand relatedIn any even vaguely-modern environment (so, not IE8) where you have access to the
Arrayfeatures added by ES5, you can useforEach(spec | MDN) if you're only dealing with synchronous code (or you don't need to wait for an asynchronous process to finish during the loop):forEachaccepts a callback function and, optionally, a value to use asthiswhen calling that callback (not used above). The callback is called for each element in the array, in order, skipping non-existent elements in sparse arrays. Although I only used one parameter above, the callback is called with three arguments: The element for that iteration, the index of that element, and a reference to the array you're iterating over (in case your function doesn't already have it handy).Like
for-of,forEachhas the advantage that you don't have to declare indexing and value variables in the containing scope; in this case, they're supplied as arguments to the iteration function, and so nicely scoped to just that iteration.Unlike
for-of,forEachhas the disadvantage that it doesn't understandasyncfunctions andawait. If you use anasyncfunction as the callback,forEachdoes not wait for that function's promise to settle before continuing. Here's theasyncexample fromfor-ofusingforEachinstead — notice how there's an initial delay, but then all the text appears right away instead of waiting:forEachis the "loop through them all" function, but ES5 defined several other useful "work your way through the array and do things" functions, including:every(spec | MDN) - stops looping the first time the callback returns a falsy valuesome(spec | MDN) - stops looping the first time the callback returns a truthy valuefilter(spec | MDN) - creates a new array including elements where the callback returns a truthy value, omitting the ones where it doesn'tmap(spec | MDN) - creates a new array from the values returned by the callbackreduce(spec | MDN) - builds up a value by repeatedly calling the callback, passing in previous values; see the spec for the detailsreduceRight(spec | MDN) - likereduce, but works in descending rather than ascending orderAs with
forEach, if you use anasyncfunction as your callback, none of those waits for the function's promise to settle. That means:asyncfunction callback is never appropriate withevery,some, andfiltersince they will treat the returned promise as though it were a truthy value; they don't wait for the promise to settle and then use the fulfillment value.asyncfunction callback is often appropriate withmap, if the goal is to turn an array of something into an array of promises, perhaps for passing to one of the promise combinator functions (Promise.all,Promise.race,promise.allSettled, orPromise.any).asyncfunction callback is rarely appropriate withreduceorreduceRight, because (again) the callback will always return a promise. But there is an idiom of building a chain of promises from an array that usesreduce(const promise = array.reduce((p, element) => p.then(/*...something using `element`...*/));), but usually in those cases afor-oforforloop in anasyncfunction will be clearer and easier to debug.3. Use a simple
forloopSometimes the old ways are the best:
If the length of the array won't change during the loop, and it's in highly performance-sensitive code, a slightly more complicated version grabbing the length up front might be a tiny bit faster:
And/or counting backward:
But with modern JavaScript engines, it's rare you need to eke out that last bit of juice.
Before ES2015, the loop variable had to exist in the containing scope, because
varonly has function-level scope, not block-level scope. But as you saw in the examples above, you can useletwithin theforto scope the variables to just the loop. And when you do that, theindexvariable is recreated for each loop iteration, meaning closures created in the loop body keep a reference to theindexfor that specific iteration, which solves the old "closures in loops" problem:In the above, you get "Index is: 0" if you click the first and "Index is: 4" if you click the last. This does not work if you use
varinstead oflet(you'd always see "Index is: 5").Like
for-of,forloops work well inasyncfunctions. Here's the earlier example using aforloop:4. Use
for-incorrectlyfor-inisn't for looping through arrays, it's for looping through the names of an object's properties. It does often seem to work for looping through arrays as a by-product of the fact that arrays are objects, but it doesn't just loop through the array indexes, it loops through all enumerable properties of the object (including inherited ones). (It also used to be that the order wasn't specified; it is now [details in this other answer], but even though the order is specified now, the rules are complex, there are exceptions, and relying on the order is not best practice.)The only real use cases for
for-inon an array are:Looking only at that first example: You can use
for-into visit those sparse array elements if you use appropriate safeguards:Note the three checks:
That the object has its own property by that name (not one it inherits from its prototype; this check is also often written as
a.hasOwnProperty(name)but ES2022 addsObject.hasOwnwhich can be more reliable), andThat the name is all decimal digits (e.g., normal string form, not scientific notation), and
That the name's value when coerced to a number is <= 2^32 - 2 (which is 4,294,967,294). Where does that number come from? It's part of the definition of an array index in the specification. Other numbers (non-integers, negative numbers, numbers greater than 2^32 - 2) are not array indexes. The reason it's 2^32 - 2 is that that makes the greatest index value one lower than 2^32 - 1, which is the maximum value an array's
lengthcan have. (E.g., an array's length fits in a 32-bit unsigned integer.)...although with that said, most code only does the
hasOwnPropertycheck.You wouldn't do that in inline code, of course. You'd write a utility function. Perhaps:
Like
for,for-inworks well in asynchronous functions if the work within it needs to be done in series.5. Use an iterator explicitly (ES2015+)
for-ofuses an iterator implicitly, doing all the scut work for you. Sometimes, you might want to use an iterator explicitly. It looks like this:An iterator is an object matching the Iterator definition in the specification. Its
nextmethod returns a new result object each time you call it. The result object has a property,done, telling us whether it's done, and a propertyvaluewith the value for that iteration. (doneis optional if it would befalse,valueis optional if it would beundefined.)What you get for
valuevaries depending on the iterator. On arrays, the default iterator provides the value of each array element ("a","b", and"c"in the example earlier). Arrays also have three other methods that return iterators:values(): This is an alias for the[Symbol.iterator]method that returns the default iterator.keys(): Returns an iterator that provides each key (index) in the array. In the example above, it would provide"0", then"1", then"2"(yes, as strings).entries(): Returns an iterator that provides[key, value]arrays.Since iterator objects don't advance until you call
next, they work well inasyncfunction loops. Here's the earlierfor-ofexample using the iterator explicitly:For Array-Like Objects
Aside from true arrays, there are also array-like objects that have a
lengthproperty and properties with all-digits names:NodeListinstances,HTMLCollectioninstances, theargumentsobject, etc. How do we loop through their contents?Use most of the options above
At least some, and possibly most or even all, of the array approaches above apply equally well to array-like objects:
Use
for-of(use an iterator implicitly) (ES2015+)for-ofuses the iterator provided by the object (if any). That includes host-provided objects (like DOM collections and lists). For instance,HTMLCollectioninstances fromgetElementsByXYZmethods andNodeLists instances fromquerySelectorAllboth support iteration. (This is defined quite subtly by the HTML and DOM specifications. Basically, any object withlengthand indexed access is automatically iterable. It doesn't have to be markediterable; that is used only for collections that, in addition to being iterable, supportforEach,values,keys, andentriesmethods.NodeListdoes;HTMLCollectiondoesn't, but both are iterable.)Here's an example of looping through
divelements:Use
forEachand related (ES5+)The various functions on
Array.prototypeare "intentionally generic" and can be used on array-like objects viaFunction#call(spec | MDN) orFunction#apply(spec | MDN). (If you have to deal with IE8 or earlier [ouch], see the "Caveat for host-provided objects" at the end of this answer, but it's not an issue with vaguely-modern browsers.)Suppose you wanted to use
forEachon aNode'schildNodescollection (which, being anHTMLCollection, doesn't haveforEachnatively). You'd do this:(Note, though, that you could just use
for-ofonnode.childNodes.)If you're going to do that a lot, you might want to grab a copy of the function reference into a variable for reuse, e.g.:
Use a simple
forloopPerhaps obviously, a simple
forloop works for array-like objects.Use an iterator explicitly (ES2015+)
See #1.
You may be able to get away with
for-in(with safeguards), but with all of these more appropriate options, there's no reason to try.Create a true array
Other times, you may want to convert an array-like object into a true array. Doing that is surprisingly easy:
Use
Array.fromArray.from(spec) | (MDN) (ES2015+, but easily polyfilled) creates an array from an array-like object, optionally passing the entries through a mapping function first. So:...takes the
NodeListfromquerySelectorAlland makes an array from it.The mapping function is handy if you were going to map the contents in some way. For instance, if you wanted to get an array of the tag names of the elements with a given class:
Use spread syntax (
...)It's also possible to use ES2015's spread syntax. Like
for-of, this uses the iterator provided by the object (see #1 in the previous section):So for instance, if we want to convert a
NodeListinto a true array, with spread syntax this becomes quite succinct:Use the
slicemethod of arraysWe can use the
slicemethod of arrays, which like the other methods mentioned above is "intentionally generic" and so can be used with array-like objects, like this:So for instance, if we want to convert a
NodeListinto a true array, we could do this:(If you still have to handle IE8 [ouch], will fail; IE8 didn't let you use host-provided objects as
thislike that.)Caveat for host-provided objects
If you use
Array.prototypefunctions with host-provided array-like objects (for example, DOM collections and such provided by the browser rather than the JavaScript engine), obsolete browsers like IE8 didn't necessarily handle that way, so if you have to support them, be sure to test in your target environments. But it's not an issue with vaguely-modern browsers. (For non-browser environments, naturally it'll depend on the environment.)