ループ処理とは?(コードの繰り返し実行)
プログラミングにおける「ループ処理(繰り返し処理)」とは、特定のコードブロックを、指定した条件が成り立っている間、何度も繰り返し実行させるための制御構造のことです。ソースでも、ループを使うと同じコードを効率よく繰り返し実行できると説明されています。
これは、前回学んだ「条件分岐(if文)」と並び、プログラムに動きのある振る舞いを与えるための重要な機能です。では、なぜループ処理が必要なのでしょうか?答えは、プログラミングの大きな目的である「自動化」と「効率化」を実現するためです。
人間は単純な繰り返し作業を続けるのが得意ではありません。例えば「コンソールに1から1000までの数字を順番に表示する」作業を想像してください。もしループがなければ、console.log(1); から console.log(1000); まで、1000行のコードを手作業で書く必要があります。これは明らかに非効率で、後から直すのも大変になります。
ループ処理を使えば、この1000行分の作業を、たった数行で表現できます。
for (let i = 1; i <= 1000; i++) {
console.log(i);
}
このように、ループ処理は「同じような処理を、条件を少しずつ変えながら何度でも実行する」ことを可能にします。配列データの処理やDOM要素のまとめ操作など、Web開発のあらゆる場面で登場する欠かせない技術です。
for文の基本構文:初期化式・条件式・増分式の3要素
JavaScriptで最も基本的で、よく使われるループがfor文です。for文は、次の3つの要素を丸括弧()の中に書き、セミコロン;で区切って作ります。
for (初期化式; 条件式; 増分式) {
// 繰り返したい処理
}
(1). 初期化式(Initialization):ループが始まるときに一度だけ実行されます。多くの場合、回数を数えるための「カウンター変数」を作り、最初の値を入れます。ソースの例では let i = 1; と書かれており、i を1からスタートさせています。
(2). 条件式(Condition):ループを続けるかどうかを決める式です。各周回の前にチェックされ、結果がtrueの間だけブロック{}の中が実行されます。falseになった瞬間にループは終わります。例では i <= 5; なので「iが5以下の間は繰り返す」という意味です。
(3). 増分式(Update/Increment):ブロック内の処理が1回終わるたびに実行される式です。通常はカウンター変数を更新します。例の i++ は「iを1増やす(インクリメントする)」という意味です。
この3つを組み合わせることで、「どこから始めるか(初期化)」「いつまで続けるか(条件)」「どう進めるか(増分)」を自分で決められるようになります。
カウンター変数の役割と命名規則(なぜ i が使われるのか)
for文の中で作り、ループ回数や位置を管理する変数を「カウンター変数」と呼びます。そして慣習として、この変数名には i がよく使われます。
なぜ i なのか?主な理由は次の通りです。
(1). Index(インデックス):配列の要素番号(添字)を表す Index の頭文字として使いやすいから。
(2). Iterator(イテレータ):繰り返し処理を進める役割を持つ Iterator の頭文字として使われるから。
(3). Integer(整数):古い言語の時代から、整数の変数として i, j, k を使う文化があったから。
もちろん、必ず i にする必要はありません。count や index のように意味が分かる名前でもOKです。特に二重ループなどで複雑になる場合は、rowIndex や colIndex のように「何の番号か」が伝わる名前の方が安全です。ただ、単純なループなら世界中で共通認識のある i を使う方が読みやすい、というのがよくある考え方です。
ループ処理の流れと実行順序の可視化
for文を理解するには、「どの順番で動くか」を追えるようになることが大切です。ソースの例 for (let i = 1; i <= 5; i++) を使って流れを分解します。
(1). 開始:プログラムがfor文に到達します。
(2). 初期化:let i = 1; が実行され、iが1になります(ここは最初の一回だけです)。
(3). 条件判定(1回目):i <= 5(1 <= 5)をチェックします。trueなのでブロック{}へ進みます。
(4). 処理実行:console.log(i) が実行され、1が表示されます。
(5). 増分:ブロックの最後に来たので i++ が実行され、iは2になります。
(6). 条件判定(2回目):i <= 5(2 <= 5)をチェックします。trueなのでまたブロックを実行します。
(7). 処理実行:2が表示されます。
(8). …繰り返し…:これを i が5になるまで繰り返します。
(9). 終了判定:i++で i が6になったとき、6 <= 5 は false になります。
(10). 終了:ループを抜けて、for文の次の行へ進みます。
ポイントは、初期化は一回だけで、その後は「条件判定 → 処理 → 増分」が繰り返されることです。
変数のスコープとlet vs var:ループ内での挙動の違い
for文の初期化式で変数を宣言するとき、var を使うか let を使うかで「変数が使える範囲(スコープ)」が大きく変わります。これはJavaScriptでは重要なポイントです。
var の場合:昔のJavaScriptでは var がよく使われていました。しかし var はブロックスコープを持たないため、for文で宣言した変数がループの外からも見えてしまいます。
for (var i = 0; i < 5; i++) { … }
console.log(i); // 5(アクセスできてしまう)
これは意図しない上書きやバグにつながりやすい挙動です。
let の場合:ES6(ECMAScript 2015)から追加された let はブロックスコープを持ちます。for文で let を使うと、その変数はループの中だけで有効になります。
for (let i = 0; i < 5; i++) { … }
console.log(i); // ReferenceError: i is not defined(エラー)
ループの外から触れないため、変数が汚れにくく安全です。現代のJavaScriptでは、カウンター変数には let を使うのが基本です。
無限ループの恐怖と回避方法
ループで特に注意したいバグが「無限ループ」です。これは条件式がずっとfalseにならず、ループが永遠に終わらない状態です。
例えば次のようなコードです。
for (let i = 1; i > 0; i++) { // 条件が常にtrue(iはずっと0より大きい)
console.log(i);
}
または増分式を書き忘れた場合:
for (let i = 1; i <= 5;) { // iが増えないので、ずっと1のまま
console.log(i);
}
無限ループになると、ブラウザが固まったりCPUが100%近くになったりして、操作不能になることがあります。ブラウザなら「ページが応答しません」が出ることもありますが、Node.jsなどサーバー側で起きると、システム全体に影響が出る可能性があります。
回避方法:
(1). 条件式を確認する:いつ終わるのかが正しく書けているか。
(2). カウンター変数の更新:iが増える/減るなど変化して、いつか終了条件に到達するかを確認する。
配列とループ処理の黄金コンビ:インデックスを使ったアクセス
ループが最もよく使われるのは、配列(Array)のデータを順番に処理するときです。配列は複数の値を順に持ち、各要素はインデックス(添字)で取り出せます。
配列のインデックスは0から始まります。また、配列の length には要素数が入っています。これらを使うと、全要素を順番に処理できます。
const hobbies = [“読書”, “旅行”, “映画”];
for (let i = 0; i < hobbies.length; i++) {
console.log(hobbies[i]);
}
解説:
(1). i を0から始めます(配列の先頭)。
(2). 条件式は i < hobbies.length にします。要素が3個ならインデックスは0,1,2なので、iが3になったら終わります。
(3). hobbies[i] で、i番目の要素を取り出します。
この書き方は、APIで取った一覧データを表示するときなどに必ず出てくる定番パターンです。
break文とcontinue文:ループの制御と中断
通常ループは条件がfalseになるまで繰り返しますが、途中で強制終了したり、その回だけ飛ばしたい場合もあります。そこで使うのが break と continue です。
break文(中断):ループ全体をそこで終わらせます。「探しているものが見つかったら、もう続けなくていい」場合に便利です。
for (let i = 0; i < 10; i++) {
if (i === 5) {
break; // ループを脱出
}
console.log(i); // 0,1,2,3,4まで表示
}
continue文(スキップ):その回の処理だけを飛ばして、次の周回へ進みます。「この値のときだけ処理したくない」場合に使います。
for (let i = 0; i < 5; i++) {
if (i === 2) {
continue; // iが2のときだけスキップ
}
console.log(i); // 0,1,3,4が表示
}
これらを使うと、必要以上に回らない効率的なループが作れます。
ネスト(入れ子)構造:多次元データの処理と計算量
for文の中に別のfor文を入れることを「ネスト(入れ子)」と呼びます。表のような2次元データ(2次元配列)を処理するときによく使います。
for (let i = 0; i < 3; i++) { // 外側のループ
for (let j = 0; j < 3; j++) { // 内側のループ
console.log(i: ${i}, j: ${j});
}
}
外側が1回回るたびに、内側は最後まで回りきります。変数名は外側が i、内側が j(さらに深いなら k)とするのが一般的です。
注意点(計算量):ネストが深いほど処理回数は急増します。配列の長さが n のとき、二重ループは n * n 回の処理になります。少量データなら問題ありませんが、データが増えると一気に遅くなるため、効率を意識する必要があります。
実践的なループ活用:DOM操作によるリスト生成
最後に、Web開発でループがどう使われるかをDOM操作の例で見てみましょう。HTMLに空の<ul>があり、JavaScriptの配列から<li>を作って追加するケースです。
<ul id=”list”></ul> const fruits = [“Apple”, “Banana”, “Cherry”]; const listElement = document.getElementById(“list”);
for (let i = 0; i < fruits.length; i++) {
// 1. 新しいli要素を作成
const newItem = document.createElement(“li”);
// 2. テキストを設定
newItem.textContent = fruits[i];
// 3. 親要素(ul)に追加
listElement.appendChild(newItem);
}
解説:
(1). document.createElement(“li”) で<li>要素を作ります。
(2). textContentで配列 fruits の文字列を<li>の中身にします。
(3). appendChildで、作った<li>を<ul>の中に追加します。
このように、ループとDOM操作を組み合わせると、データ数に応じて画面表示を自動生成・更新できます。ToDoリストや商品一覧など、多くのWebサービスの基本になる技術です。
