MENU

JavaScript構文クロージャ

【JavaScript】 forループとiカウンター変数の秘密

更新日:2021/01/20

 

■お願い
去年ECMAScript2020を頑張って日本語訳しましたが、誰も見てくれません・・・
誰かみて!!
【JavaScript】 学習のためECMAScript2020を日本語訳してみました

余談:カウンターにiを使うのはなぜ?

 

JavaScriptでforループの解説をしているテキスト等には、例として次のようなコードが掲載されていることが多いです。

 


for( let i = 0 ; i < 10 ; i++ ) {
   // …繰り返し処理
}

 

「ほう、そうなんだ」と解説を読み進めるわけですが、一部の細かいことを気にしちゃう系の人はこう思うはずです。

 

「なんでカウンター変数にiを使っているの?」

 

そうなんです。
多くの解説記事やサンプルプログラム、はては実コードまで、みんなiを使っています。

 

僕も一時期、『変数名には意味があるものを使用するべきだ』という意見を聞いて、stepとかcounterとか使っていましたが、「なんで企業コーディング規約に縛られているわけでもないのに、めんどくさいことしてるんだろう」と思って、いまだにiを使用しています。

 

僕は今から20年以上前にNECのPC9801というパソコンとボーランド社のTurbo CというCコンパイラをバイトして購入して、毎日C言語で遊んでいました。
懐かしい思い出です。

 

そしてその頃は既に、for分のカウンターはiが一般的に使用されていたのです。
僕も何も疑問に思わずにiを使用するわけです。
そして血肉となってしみ込んだ結果、無意識のうちにiを使用するようになったわけです。

 

同じ人多いのではないでしょうか?

 

そんな僕らが後輩たちにプログラミング言語を教えたり、書籍やWeb記事にコードを掲載するときもiを使用します。
教えられたり記事を見た人は疑問もなく受け入れ、後世へと伝えられていくのです。

 

今気が付きましたが、forループでiを使うのは、既に文化になっているのですね。
仕方がないことのようです。

 

ここまでは余談の余談でした。

 

そもそも、どうしてiが使用されていたのかという疑問の答えになっていません。

 

調べてみると次の記事がヒットしました。

 

なぜループカウンタ変数のほとんどに “i”が使用されるのか?(外部記事)

 

始まりはFORTRANというプログラミング言語で、この言語では変数名は一文字だけしか使用できなくて、そのうち整数をあらわすものはI~Nの6個のみだったんだそうです。

 

FORTRANにはfor文がないようですが、FORTRANを学んだ人が他言語を利用するようになり、今までの癖で変数名にiを使用していたのが、後々まで広まってしまったのが現状かもしれません。

forループとiカウンターとクロージャ

 

余談が長すぎました。
本題です。

 

JavaScriptは関数の中で関数を定義できる不思議ちゃんな言語です。

 

そしてforループ内で関数を定義し、それを外部に持ち出すとき少し注意が必要です。

 

letのforループ

 

次のコードを見てください。

 


const func = [];

for( let i = 0 ; i < 5 ; i ++ ){
    func.push( ()=>{
       console.log( i );
    });
}

func[0](); // 0
func[1](); // 1
func[2](); // 2
func[3](); // 3
func[4](); // 4

 

forループ内でアロー関数を定義し、それを配列に格納しています。
つまり、forループ内で関数を定義し、それを外部に持ち出しています。

 

持ちだした関数はクロージャを形成しています。
実行すると、定義された時点でのカウンター変数iの値をコンソールに出力します。

 

感覚的には当たり前の動作です。

 

varのforループ

 

上のコードを少し書き換えてみます。

 


const func = [];

for( var i = 0 ; i < 5 ; i ++ ){
    func.push( ()=>{
       console.log( i );
    });
}

func[0](); // 5
func[1](); // 5
func[2](); // 5
func[3](); // 5
func[4](); // 5

 

カウンター変数iの定義をletからvarに変更しました。
letとvarの違いについては、ここでは振れないので次の記事を見てください。
【JavaScript】 適当に使ってた!変数宣言var/let/constの使い分け

 

関数を実行すると、全て5と出力されてしまいました。

 

実はvarは関数スコープなので、関数内で一つしか定義できません。
そのため次のようなコードと同等となります。

 


var i;
for( i = 0 ; i < 5 ; i ++ ){
    func.push( ()=>{
       console.log( i );
    });
}

 

 

forループ終了時点で i は 5 になっています。
そのためfunc配列内の関数を実行すると、全て5と表示されます。

 

letでカウンター変数iが保持される理由

 

一方letはブロックスコープで定義されます。
そしてforで定義したカウンター変数iは、後に続くブロック({ })内で個別に評価されます。

 

iの値コードイメージ
0
{
 let i = 0;
 func.push( ()=>{
       console.log( i );
    });
}
1
{
 let i = 1;
 func.push( ()=>{
       console.log( i );
    });
}
・・・

・・・

 

または、次のように関数が呼ばれているイメージでもいいかもしれません。

 


for( let i = 0 ; i < 5 ; i ++ ) (function( i ){
    func.push( ()=>{
       console.log( i );
    });
})( i );

 

変数iが個別にクロージャの環境レコードに取り込まれるので、実行時の値を保持できているわけです。

 

上のコード、よくわからないという人は次の記事を読んでみてください。
【JavaScript】 即時関数の挙動について調べてみた

 

varは使用しない

 

古いバージョンのJavaScriptにはletが存在せず、varのみでした。

 

そのため、ここで紹介したletでの結果を得るために、いろいろなテクニックが考案されました。
今でもネットで検索すると出てきます。
そして、それを読んだ初心者がletで定義したときも同じ結果になると勘違いして、混乱します。
というか、僕は混乱しました。

 

letを使用するば全く気にしなくていいテクニックです。

 

そもそもvarが使いにくいということでletがJavaScriptに追加された経緯があります。
今ではvarの使用を推奨していない人が多いです。

 

僕もあまりおススメしません。

記事の内容について

 

こんにちはけーちゃんです。
説明するのって難しいですね。


「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

そんなときは、ご意見もらえたら嬉しいです。

ご意見はこちら。
https://note.affi-sapo-sv.com/info.php

【お願い】

お願い

■このページのURL


■このページのタイトル


■リンクタグ