How can I loop through all the entries in an array using JavaScript?
ベストアンサー1
TL;DR
Your best bets are usually
- a
for-of
loop (ES2015+ only; spec | MDN) - simple andasync
-friendlyfor (const element of theArray) { // ...use `element`... }
forEach
(ES5+ only; spec | MDN) (or its relativessome
and such) - notasync
-friendly (but see details)theArray.forEach(element => { // ...use `element`... });
- a simple old-fashioned
for
loop -async
-friendlyfor (let index = 0; index < theArray.length; ++index) { const element = theArray[index]; // ...use `element`... }
- (rarely)
for-in
with safeguards -async
-friendlyfor (const propertyName in theArray) { if (/*...is an array element property (see below)...*/) { const element = theArray[propertyName]; // ...use `element`... } }
- a
Some quick "don't"s:
- Don't use
for-in
unless you use it with safeguards or are at least aware of why it might bite you. - Don't use
map
if you're not using its return value.
(There's sadly someone out there teachingmap
[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
.) - Don't use
forEach
if the callback does asynchronous work and you want theforEach
to wait until that work is done (because it won't).
- Don't use
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 arguments
object, 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"):
- Use
for-of
(use an iterator implicitly) (ES2015+) - Use
forEach
and related (ES5+) - Use a simple
for
loop - Use
for-in
correctly - Use an iterator explicitly (ES2015+)
(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, Map
s, and Set
s, as well as DOM collections and lists, as you'll see later). Iterable objects provide iterators for their values. The new for-of
statement loops through the values returned by an iterator:
const a = ["a", "b", "c"];
for (const element of a) { // You can use `let` instead of `const` if you like
console.log(element);
}
// a
// b
// c
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 element
is scoped to each loop iteration; trying to use element
after the end of the loop would fail because it doesn't exist outside the loop body.
In theory, a for-of
loop 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 for forEach
[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-of
is entirely async
-friendly. If you need the work in a loop body to be done in series (not in parallel), an await
in the loop body will wait for the promise to settle before continuing. Here's a silly example:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
for (const message of messages) {
await delay(400);
console.log(message);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects
Note how the words appear with a delay before each one.
It's a matter of coding style, but for-of
is the first thing I reach for when looping through anything iterable.
2. Use forEach
and related
In any even vaguely-modern environment (so, not IE8) where you have access to the Array
features added by ES5, you can use forEach
(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):
const a = ["a", "b", "c"];
a.forEach((element) => {
console.log(element);
});
forEach
accepts a callback function and, optionally, a value to use as this
when 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
, forEach
has 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
, forEach
has the disadvantage that it doesn't understand async
functions and await
. If you use an async
function as the callback, forEach
does not wait for that function's promise to settle before continuing. Here's the async
example from for-of
using forEach
instead — notice how there's an initial delay, but then all the text appears right away instead of waiting:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
// INCORRECT, doesn't wait before continuing,
// doesn't handle promise rejections
messages.forEach(async message => {
await delay(400);
console.log(message);
});
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects
forEach
is 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 order
As with forEach
, if you use an async
function as your callback, none of those waits for the function's promise to settle. That means:
- Using an
async
function callback is never appropriate withevery
,some
, andfilter
since 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. - Using an
async
function 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
). - Using an
async
function callback is rarely appropriate withreduce
orreduceRight
, 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-of
orfor
loop in anasync
function will be clearer and easier to debug.
3. Use a simple for
loop
Sometimes the old ways are the best:
const a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
const element = a[index];
console.log(element);
}
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:
const a = ["a", "b", "c"];
for (let index = 0, len = a.length; index < len; ++index) {
const element = a[index];
console.log(element);
}
And/or counting backward:
const a = ["a", "b", "c"];
for (let index = a.length - 1; index >= 0; --index) {
const element = a[index];
console.log(element);
}
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 var
only has function-level scope, not block-level scope. But as you saw in the examples above, you can use let
within the for
to scope the variables to just the loop. And when you do that, the index
variable is recreated for each loop iteration, meaning closures created in the loop body keep a reference to the index
for that specific iteration, which solves the old "closures in loops" problem:
// (The `NodeList` from `querySelectorAll` is array-like)
const divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
divs[index].addEventListener('click', e => {
console.log("Index is: " + index);
});
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
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 var
instead of let
(you'd always see "Index is: 5").
Like for-of
, for
loops work well in async
functions. Here's the earlier example using a for
loop:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
for (let i = 0; i < messages.length; ++i) {
const message = messages[i];
await delay(400);
console.log(message);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects
4.for-in
正しく使用する
for-in
は配列をループするためのものではなく、オブジェクトのプロパティの名前をループするためのものです。配列がオブジェクトであるという事実の副産物として、配列をループするためによく機能するように見えますが、配列のインデックスをループするだけでなく、オブジェクトのすべての列挙可能なプロパティ(継承されたものを含む)をループします。(また、以前は順序が指定されていませんでした。現在は[詳細はこの他の答え] ですが、順序が指定されているとはいえ、ルールは複雑で、例外もあり、順序に頼るのはベストプラクティスではありません。
for-in
配列でのの実際の使用例は次のとおりです。
- それは疎な配列大きな隙間があったり、
- 配列オブジェクトで非要素プロパティを使用しており、それをループに含める必要がある
最初の例だけを見ると、for-in
適切な安全策を講じれば、を使用してそれらのスパース配列要素にアクセスできます。
// `a` is a sparse array
const a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (const name in a) {
if (Object.hasOwn(a, name) && // These checks are
/^0$|^[1-9]\d*$/.test(name) && // explained
name <= 4294967294 // below
) {
const element = a[name];
console.log(a[name]);
}
}
次の 3 つのチェックに注意してください。
オブジェクトがその名前の独自のプロパティを持っていること(プロトタイプから継承したものではないこと。このチェックは次のようにも書かれることが多いが、
a.hasOwnProperty(name)
ES2022では次のように追加されている。Object.hasOwn
より信頼性が高い)名前はすべて10進数であること(例:科学的記数法ではなく通常の文字列形式)、および
名前の値を数値に強制変換すると、<= 2^32 - 2 (4,294,967,294) になります。この数字はどこから来るのでしょうか? これは配列インデックスの定義の一部です。仕様書ではその他の数値 (非整数、負の数、2^32 - 2 より大きい数) は配列のインデックスではありません。2^32 - 2である理由は、最大のインデックス値が配列が持つことができる最大値である2^32 - 1より 1 小さくなるためです
length
。(たとえば、配列の長さは 32 ビットの符号なし整数に収まります。)
...とはいえ、ほとんどのコードはhasOwnProperty
チェックのみを実行します。
もちろん、インライン コードでこれを行うことはありません。ユーティリティ関数を記述します。おそらく次のようになります。
// Utility function for antiquated environments without `forEach`
const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
const rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
for (const name in array) {
const index = +name;
if (hasOwn(a, name) &&
rexNum.test(name) &&
index <= 4294967294
) {
callback.call(thisArg, array[name], index, array);
}
}
}
const a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";
sparseEach(a, (value, index) => {
console.log("Value at " + index + " is " + value);
});
と同様にfor
、は、for-in
その内部の作業を順番に実行する必要がある場合、非同期関数内で適切に機能します。
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
for (const name in messages) {
if (messages.hasOwnProperty(name)) { // Almost always this is the only check people do
const message = messages[name];
await delay(400);
console.log(message);
}
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects
5. イテレータを明示的に使用する (ES2015+)
for-of
は暗黙的にイテレータを使用し、すべての雑務を代わりに行います。 場合によっては、イテレータを明示的に使用したいこともあります。 次のようになります。
const a = ["a", "b", "c"];
const it = a.values(); // Or `const it = a[Symbol.iterator]();` if you like
let entry;
while (!(entry = it.next()).done) {
const element = entry.value;
console.log(element);
}
イテレータは、仕様の Iterator 定義に一致するオブジェクトです。そのメソッドは、呼び出すたびにnext
新しい結果オブジェクトを返します。結果オブジェクトには、完了したかどうかを示すプロパティ と、その反復処理の値を持つdone
プロパティがあります。(の場合は はオプション、の場合は はオプションです。)value
done
false
value
undefined
で得られる結果は、イテレータによって異なります。配列の場合、デフォルトのイテレータは各配列要素 (前の例では、、、)value
の値を提供します。配列には、イテレータを返す他の 3 つのメソッドもあります。"a"
"b"
"c"
values()
[Symbol.iterator]
: これは、デフォルトのイテレータを返すメソッドのエイリアスです。keys()
: 配列内の各キー(インデックス)を提供するイテレータを返します。上記の例では、 、 、 (文字列ではなく数値として)を提供します0
。1
(2
また、スパース配列では、意思存在しない要素のインデックスを含めます。entries()
: 配列を提供する反復子を返します[key, value]
。
イテレータ オブジェクトは を呼び出すまで進まないためnext
、関数ループでうまく機能しますasync
。以下は、for-of
イテレータを明示的に使用する前の例です。
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function showSlowly(messages) {
const it = messages.values()
while (!(entry = it.next()).done) {
await delay(400);
const element = entry.value;
console.log(element);
}
}
showSlowly([
"So", "long", "and", "thanks", "for", "all", "the", "fish!"
]);
// `.catch` omitted because we know it never rejects
配列のようなオブジェクトの場合
真の配列の他に、すべて数字の名前を持つプロパティとプロパティを持つ配列のようなオブジェクトもあります。length
NodeList
インスタンス、HTMLCollection
インスタンス、arguments
オブジェクトなど。その内容をループするにはどうすればよいでしょうか。
上記のオプションのほとんどを使用する
上記の配列アプローチの少なくとも一部、おそらくほとんど、あるいはすべてが、配列のようなオブジェクトにも同様に適用されます。
使用
for-of
(暗黙的にイテレータを使用する)(ES2015+)for-of
使用反復子オブジェクトによって提供されるもの(ある場合)です。これには、ホストが提供するオブジェクト(DOM コレクションやリストなど)が含まれます。たとえば、メソッドHTMLCollection
からのインスタンスと の両方からのインスタンスは反復をサポートします。(これは、HTML および DOM 仕様によって非常に微妙に定義されています。基本的に、およびインデックス アクセスを持つオブジェクトは自動的に反復可能です。をマークする必要はありません。これは、反復可能であることに加えて、、、、およびメソッドをサポートするコレクションにのみ使用されます。は をサポートし、は をサポートしませんが、どちらも反復可能です。)getElementsByXYZ
NodeList
querySelectorAll
length
iterable
forEach
values
keys
entries
NodeList
HTMLCollection
div
要素をループする例を次に示します。
const divs = document.querySelectorAll("div");
for (const div of divs) {
div.textContent = Math.random();
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
使用
forEach
と関連(ES5+)のさまざまな関数は
Array.prototype
「意図的に汎用的」であり、配列のようなオブジェクトに対してFunction#call
(スペック|翻訳) またはFunction#apply
(スペック|翻訳)。(IE8 以前を扱う必要がある場合 [痛い]、この回答の最後にある「ホスト提供のオブジェクトに関する注意事項」を参照してください。ただし、これは比較的新しいブラウザでは問題になりません。)のコレクション ( であるため、ネイティブにはがありません)
forEach
でを使用したいとします。次のようにします。Node
childNodes
HTMLCollection
forEach
Array.prototype.forEach.call(node.childNodes, (child) => { // Do something with `child` });
(ただし、
for-of
on を使用することもできますnode.childNodes
。)これを頻繁に行う場合は、関数参照のコピーを変数に保存して再利用することをお勧めします。例:
// (This is all presumably in a module or some scoping function) const forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); // Then later... forEach(node.childNodes, (child) => { // Do something with `child` });
シンプルな
for
ループを使用するおそらく明らかなことですが、
for
配列のようなオブジェクトでは単純なループが機能します。イテレータを明示的に使用する (ES2015+)
#1を参照してください。
安全策を講じれば、何とかなるかもしれませんfor-in
が、より適切な選択肢がたくさんあるので、試す理由はありません。
真の配列を作成する
場合によっては、配列のようなオブジェクトを真の配列に変換したいこともあります。これは驚くほど簡単です。
使用
Array.from
Array.from
(スペック)|(MDN)(ES2015+ ですが、簡単にポリフィルできます) 配列のようなオブジェクトから配列を作成し、オプションでエントリを最初にマッピング関数に渡します。つまり、const divs = Array.from(document.querySelectorAll("div"));
NodeList
...から取得しquerySelectorAll
、そこから配列を作成します。マッピング関数は、何らかの方法でコンテンツをマッピングする場合に便利です。たとえば、特定のクラスの要素のタグ名の配列を取得したい場合:
// Typical use (with an arrow function): const divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName); // Traditional function (since `Array.from` can be polyfilled): var divs = Array.from(document.querySelectorAll(".some-class"), function(element) { return element.tagName; });
スプレッド構文を使用する(
...
)ES2015のスプレッド構文。
for-of
これは、反復子オブジェクトによって提供されるもの(前のセクションの #1 を参照):const trueArray = [...iterableObject];
たとえば、a を
NodeList
真の配列に変換したい場合、スプレッド構文を使用すると非常に簡潔になります。const divs = [...document.querySelectorAll("div")];
slice
配列メソッドを使用する私たちは
slice
配列のメソッドは、上記の他のメソッドと同様に「意図的に汎用的」であるため、次のように配列のようなオブジェクトで使用できます。const trueArray = Array.prototype.slice.call(arrayLikeObject);
たとえば、 a を
NodeList
真の配列に変換したい場合は、次のようにします。const divs = Array.prototype.slice.call(document.querySelectorAll("div"));
(まだ IE8 を扱わなければならない場合 [痛い]、失敗します。IE8 では、ホストが提供するオブジェクトをそのように使用できませんでした
this
。)
ホストが提供するオブジェクトに関する注意事項
ホストが提供するArray.prototype
配列のようなオブジェクト (たとえば、JavaScript エンジンではなくブラウザーが提供する DOM コレクションなど)で関数を使用する場合、IE8 などの古いブラウザーでは必ずしもそのように処理されないため、それらをサポートする必要がある場合は、ターゲット環境で必ずテストしてください。ただし、最新のブラウザーでは問題になりません。(ブラウザー以外の環境の場合は、当然、環境によって異なります。)