プログラミング学習の序盤で必ず習う繰り返し処理が理解できると「プログラミングやってるなぁ」と自覚できますよね。
繰り返し処理そのものは理解できる人が多いのですが、繰り返し処理の文法はなぜか何種類もあって自分で使うときに悩んだりしませんか?
とりあえずググって出てきたコードをコピペしたいけど自分のやりたいことと若干ズレていたりなども。。。
今回はJavaScriptに関する繰り返し処理のパターンを改めて調べてみたいと思います。
なんとなく理解から「納得して使う」ようになると更に成長できるので最後まで読んで言ってください。
また動画もあるので必要に応じて活用してください。
JavaScriptのforループの使い方
本記事では配列の中の要素を繰り返しコンソールに表示することを題材にします、まずforループから解説していきます。
forループの特徴は以下の通りです。
繰り返し処理の中でインデックス番号を使用できる
繰り返す条件を自分で決めることができる
配列でなくても使用できる
どういうことか以下の例題をもとに見ていきましょう。
const array = ["AAA", "BBB", "CCC"];
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}

forループは引数に3つの設定を書くことになっていて、
・繰り返し処理の中で使う変数の宣言
・繰り返す条件
・繰り返す量
となることが一般的に多いです。
上記のコードだと変数 i を配列のインデックス番号や繰り返す量に使用しています。
また i < array.lengthという書き方は「配列の中にある要素の数」となっていて、言い換えると「配列の中をすべて実行」となります。
今回は配列の中のものをすべて実行しましたが、不等号などを活用して部分的に実行することも可能です。
更に i++ についてはJavaScript特有の書き方ですが、「i = i + 1」と同じ意味です。
「変数 i を1ずつ足していく」ということですね。
加えて後続で紹介するfor inループやfor ofループは配列で使う想定になっていますが、forループは扱うデータは配列じゃなくても大丈夫です。
何かと柔軟性があるのがforループなので最初に習うことが多いと思います。
JavaScriptのfor inループの使い方
続いてfor inループの使い方です、特徴としては以下の通りです。
配列はインデックス番号を使用して原則すべての要素を実行する
オブジェクトはキーを使用して原則すべての値を実行する
こちらも例題を見ていきましょう。
const array = ["AAA", "BBB", "CCC"];
for (let i in array) {
console.log(array[i]);
}

forループより引数の設定内容が少ないですね、let i は「変数 i 」という意味でインデックス番号が入る仕組みです。
またforループと違ってfor inループは繰り返しの途中で処理を強制的に終了させることはできますが、forループのように細かく条件を作り込むことができません。
またfor inループはオブジェクトの場合だとインデックス番号ではなくキーを元に実行します。
const obj = { id: "0001", name: "AAA", age: 20 };
for (let key in obj) {
console.log(`${key},${obj[key]}`);
}

JavaScriptのfor ofループの使い方
似たようなパターンでfor ofループもありますね、for ofループの特徴は以下の通りです。
配列の要素を元にして原則すべての要素を実行する
オブジェクトには使用できない
こちらも例題で見ていきましょう。
const array = ["AAA", "BBB", "CCC"];
for (let item of array) {
console.log(item);
}

これまでと同じ結果になるのですが、インデックス番号ではなく「AAA」など中身の要素を起点に実行します。
そのため繰り返し処理の中でインデックス番号が使用できないので、インデックス番号が必要な処理を行いたい場合にはfor inループの方が適しています。
またfor ofループはfor inループと同じで繰り返しの途中で強制的に終了させることはできますが、自分で細かく繰り返しの条件を決めることができません。
更にfor ofループはオブジェクトには使用できません。
const obj = { id: "0001", name: "AAA", age: 20 };
for (let item of obj) {
console.log(item);
}

JavaScriptのforEachメソッドの使い方
最後にforEachについてです、特徴としては以下の通りです。
繰り返す単位はインデックス番号でも要素でもOK
繰り返す条件を自分で決めることができる
処理を途中で矯正終了することができない
オブジェクトには使用できない
こちらも例題を元に見ていきましょう。
const array = ["AAA", "BBB", "CCC"];
array.forEach((item) => {
console.log(item);
});

一番シンプルなコードでfor ofループとほとんど同じですね、オブジェクトには使えない点も同じです。
const obj = { id: "0001", name: "AAA", age: 20 };
obj.forEach((key) => {
console.log(obj[key]);
});

上記のコードは配列に対して要素を起点に繰り返していますがインデックス番号も使用することができます。
const array = ["AAA", "BBB", "CCC"];
array.forEach((item, i) => {
console.log(`${item},${i}`);
});

更にforEachは処理の中で細かく繰り返す条件を作ることができます。
const array = ["AAA", "BBB", "CCC"];
array.forEach((item, i) => {
if (i < 1) {
console.log(item);
}
});

ちなみにforEachは厳密にはループというよりはメソッドになります。
他の3つと違ってコールバック関数の引数としてインデックス番号や要素を使っているだけです。
そのためfor inループとfor ofループのように繰り返しの途中で強制的に終了させることはできませんので注意してください。
forEachは配列専門のメソッドのため使えないケースがある
またHTMLCollectionという配列風オブジェクトにはforEachを使用できません。
例えば以下のようなHTMLがあったとします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script
src="https://yubinbango.github.io/yubinbango/yubinbango.js"
charset="UTF-8"
></script>
<script src="./script.js" defer></script>
</head>
<body>
<div class="text">テキストA</div>
<div class="text">テキストB</div>
<div class="text">テキストC</div>
</body>
</html>
class=”text”という要素が3つあるので全ての要素をJavaScript側で取得するのにquerySelectorAllを使うことがあります。
取得したものからテキストを全て取り出すのに繰り返し処理が必要で今回紹介しているforEachを使うと以下のような感じになります。
const text1 = document.querySelectorAll('.text');
console.log(text1);
text1.forEach((txt) => {
console.log(txt.textContent);
});

これで良いのですがquerySelectorAllの部分をgetElementsByClassNameで使った場合はエラーになります。
理由としてはquerySelectorAllで取得した要素はNodeListという配列なのですが、getElementsByClassNaemで取得した要素はHTMLCollectionという配列風オブジェクトのためです。
見た目が似ているのですが実は違う種類のものでforEachをgetElementsByClassNameで取得したものには使用できない仕様になっています。
const text2 = document.document.getElementsByClassName('text');
console.log(text2);
text2.forEach((txt) => {
console.log(txt.textContent);
});

getElementsByClassNameで同じような繰り返し処理をしたい場合は前章で紹介したfor-ofを使うと解決します。
const text2 = document.document.getElementsByClassName('text');
console.log(text2);
// ここを修正
for (const txt of text2) {
console.log(txt.textContent);
}

配列と配列風オブジェクト(HTMLCollection)は見た目が似ているので最初は見て比べてみると良いでしょう。

getElementsByClassNameで取得したHTMLCollectionは配列風オブジェクトで、いわゆるオブジェクトの特徴が強く、forEachは配列専門に作られたメソッドなので使用できません。
もう一つの方法として一度配列に変換することでも解決できます。
const text2 = document.document.getElementsByClassName('text');
console.log(text2);
// ここを修正
Array.from(text2).forEach((txt) => {
console.log(txt.textContent);
});

配列じゃ無いものを配列に変換するという考え方は他のケースでも応用できますので覚えておきましょう。
また3つ目の方法としてObject.keysを使用するものがあります。
Object.keysを使用することで配列風オブジェクトのキーだけを配列にして返します。
const text2 = document.document.getElementsByClassName('text');
console.log(text2);
// ここから追加
const keys = Object.keys(text2);
console.log(keys);

こちらの[“0″,”1″,”2”]の方は配列でforEachが使えるので以下のようにすることができて、結果的に繰り返し処理でテキストを取り出すことができます。
const text2 = document.document.getElementsByClassName('text');
console.log(text2);
const keys = Object.keys(text2);
console.log(keys);
// ここを修正
keys.forEach((key) => {
console.log(text2[key].textContent);
});

JavaScriptで使う繰り返し処理の代表例は以上の4つですが、どれが一番正しいということではありません。
それぞれ特徴が細かく違うので自分がやりたい内容に合わせて使い分けることになります。
初学者の方だといきなり細かい所まで頭に入らないと思いますが、「似ているようでどれも違うんだな」という認識でいることが大事ですね。