javascriptにはforやforeachなど繰り返しループする処理のためのコードがたくさんあります。コードごとに想定されている用途は異なりますが、どのコードでも同じようなループ処理を行うことが可能です。
一見似たようなコードですが、それぞれの違いはどこにあるのか、どれを使うのが最適なのか、処理速度に焦点を合わせながら比較し、コードを紹介しながらおすすめを紹介していきます。
JavaScriptでforeachするためのコード
Javascriptでforeachをするためのコードを、今回は5パターンご紹介します。
紹介するコードはすべて同じ処理結果になります。
for
ループ処理の基本となるコードです。
処理の最後にキーが返ってくるため、 data[i]のように処理の中でキーにアクセスして、データを取得する必要があります。
基本形はこのように記述します。
// ループ処理したい構文
}
サンプル
const data = [1,2,3,4,5];
for (let i=0; i<data.length; i++) {
console.log(data[i]);
}
for in
指定したオブジェクトに存在するプロパティ名を順不同で取り出し、inの前に定義した変数に格納して処理を実行します。
基本形はこのように記述します。
// ループ処理したい構文
}
サンプル
const data = [1,2,3,4,5];
for (key in data) {
console.log(data[key]);
}
for同様にキーが返ってくるため、 data[key]のようにアクセスしてデータを取得する必要があります。
この構文ではdataオブジェクトにあるプロパティを取り出し、keyに格納しています。
for of
for-inはプロパティ名を取得しますが、for-of文はプロパティの値を取得します。ECMAScript2015から導入された、for構文の中では新しいコードです。
配列、文字列、マップ、DOMなどの繰り返し処理に用いられ、ループ処理を行うことを前提としたオブジェクトが扱えます。
基本形はこのように記述します。
// ループ処理したい構文
}
サンプル
const data = [1,2,3,4,5];
for (value of data) {
console.log(value);
}
forEach
配列の繰り返し処理にだけ使えるコードで、使用できる条件が限定されている分、他のfor構文と比較して配列処理に強いのが特徴です。
基本形はこのように記述します。
array.forEach( コールバック関数 )
forEach文のコールバック関数は「value」、「index」、「array」の3つの引数を受け取ることができます。配列要素を同時に出力したり、それぞれ別の処理を行うこともできるので、活用範囲が広がります。
連想配列(object)や繰り返し処理を中断するbreakやcontinueが使用できない点に注意してください。
サンプル
const data = [1,2,3,4,5];
data.forEach((value,index,array) => {
console.log(value);
});
jQuery.each
jQueryを使用した繰り返し処理構文です。ほかのfor構文と異なる点は、HTML要素に対して繰り返し処理が行える部分です。通常のforeach処理だけでなく、一例としてHTMLで記述されたリスト(<li></li>タグなど)から要素を取り出し、別の出力を行うことができます。
基本形はこのように記述します。
//繰り返し処理を記述する
})
サンプル
// jQuery.each
const data = [1,2,3,4,5];
$.each(data,(index,value) => {
console.log(value);
});
大まかなforの使い分けの目安
回数が決まっている→for
オブジェクトをメインに扱う→for-in
配列をメインに扱う→forEach
HTML構文とともにデザインを含む処理をする→jQuery.each
ほとんどの場合、同じ結果を得るだけであればどれを使っても大差ないと言えますが、あえて大まかにわけるとこのようになります。(不要なオブジェクトにアクセスしてしまうなどのデメリットがある場合もあります)
なお、for-ofは導入が新しいため、2015年以前にリリースされたバージョンが古いブラウザは動作しないことがあります。使用する場合は注意してください。
JavaScriptのforeach系の速度比較
用途や他のコードに合わせて選ぶことが前提になりますが、同じ処理をした場合、速度にはどれくらいの違いが出るのでしょうか。
ここでは実際に10万桁の配列データを生成させ、その速度を計測してみたいと思います。
以下のソースコードを利用して10万行のデータを処理するのにどれだけ速度が変化するか、実際に実行して検証します。
なお検証にはChrome Developer Tool(2019年11月20日時点の最新版)を使用しました。また環境は以下の通りとなります。
補足
- 実行に際して、特に最初の数回だけとても高い数値になる場合があったため、今回は最初に10回空打ちしてから計測を行なっています。
- 多くの場合ループして使いたいものは配列のデータと思われますので、今回はデータを取り出すまでの速度で比較します。
検証ソースコード
const data = […Array(100000).keys()];
const myForeach1 = (d) => {
let val = 0;
const start = performance.now();
const max = d.length;
for (let i=0; i<max; i++) {
val += d[i];
}
const end = performance.now();
console.log(end – start);
}
myForeach1(data); //10回実行
const data = […Array(100000).keys()];
let myForeach2 = (d) => {
let val = 0;
const start = performance.now();
for (key in d) {
val += d[key];
}
const end = performance.now();
console.log(end – start);
}
myForeach2(data); //10回実行
const data = […Array(100000).keys()];
let myForeach3 = (d) => {
let val = 0;
const start = performance.now();
for (value of d) {
val += value;
}
const end = performance.now();
console.log(end – start);
}
myForeach3(data); //10回実行
const data = […Array(100000).keys()];
let myForeach4 = (d) => {
let val = 0;
const start = performance.now();
d.forEach((value,index,array) => {
val += value;
});
const end = performance.now();
console.log(end – start);
}
myForeach4(data); //10回実行
const data = […Array(100000).keys()];
let myForeach5 = (data) => {
let val = 0;
const start = performance.now();
$.each(data,(index,value) => {
val += value;
});
const end = performance.now();
console.log(end – start);
}
myForeach5(data); //10回実行
計測結果
2.630000002682209
4.424999991897494
3.6600000457838178
1.9599999650381505
2.8249999741092324
2.579999971203506
2.624999964609742
2.3200000287033617
2.154999994672835
2.199999988079071
平均: 2.73s
14.38499998766929
16.254999965894967
14.26500000525266
15.769999998155981
17.460000002756715
18.149999959859997
12.705000001005828
19.940000027418137
15.989999985322356
15.279999992344528
平均:16.02s
6.110000016633421
0.18500001169741154
7.225000008475035
0.12500002048909664
9.375000023283064
0.1700000138953328
7.395000022370368
0.24500000290572643
5.910000007133931
0.12500002048909664
平均:3.68s
3.764999972190708
2.8150000143796206
4.524999996647239
4.50999999884516
3.8750000530853868
3.5700000007636845
5.004999984521419
4.075000004377216
3.23000003118068
4.569999990053475
平均:3.99s
11.455000028945506
10.07499999832362
7.8699999721720815
10.744999977760017
9.934999980032444
10.904999973718077
8.625000016763806
10.500000033061951
10.925000009592623
10.450000001583248
平均:10.14s
検証項目 | 平均値 | 順位 |
---|---|---|
for | 2.73s | 1位 |
for in | 16.02s | 5位 |
for of | 3.68s | 2位 |
forEach | 3.99s | 3位 |
jQuery.each | 10.14s | 4位 |
シンプルなコードで実行してみましたが、大方の予想通りforが早いという結果になりました。もっともこの量の配列をjavascript上で、さらにブラウザ上で実行することはまれであり、件数が減ればfor以外だめというわけではなく、誤差に近い範囲になってくるものもあります。月並みですが、やはり目的に応じて使い分けるのが一番と言えるでしょう。
また今回の検証では、for of の数値がまるでキャッシュしているかのような挙動をとりました。試しに検証環境とは違う新しいPC(メモリは同じだがCPUがcore i7)で実行したところ、このような挙動はみられませんでした(平均して早くなった)。コードを少し修正しても同じ挙動がみられたことから、検証環境のスペック不足から発生していることも考えられます。
平均値としてみると、for ofは優秀であり、近年使用している方も増えてきていますので、今回はあえて検証データをそのまま載せさせていただくことにいたしました。
選択の際は速さ以外の部分にも注目しよう
for や foreach など、ループ処理をするためのコードを検証してみましたが、複雑なオブジェクトや配列の処理を含まない場合、forが一番早いという結果になりました。
ただし、用途によってケースバイケースであり、一概にforがもっとも優れたループ処理構文であるというわけではありません。
プログラムは処理の速さはもちろん重要ですが、目的に合わせた選択も重要です。forは処理速度の速いコードではありますが、複雑な処理をさせるためには他のコードよりも構文が長くなってしまいます。
また、jQuery.eachなどのWebデザインに特化した独自の機能を持つコードもあります。コードは用法に合わせて適切に使用することで真価を発揮するため、なんの処理を行うのか目的をはっきりと考え、処理全体を設計することが大切です。