改めてスコープを理解する


こんにちは櫻井です。ちょっと最近スコープの概念が迷走してきたので今日は復習です。
特に訳わからんくなっているのがfor文で宣言した「var i = 0」がどこからどこまで有効なのかところです。今日は基本的なスコープの考え方を復習しつつこの疑問を解決していこうと思います。

スコープとは

そもそもスコープとはなんなのか。この辺の理解が間違ってると他も全部間違ってくるのでここから振り返ります。
スコープとは変数や関数が使える範囲のことです。
使える範囲限定しなくてもどこからでも使えるようにしたほうが便利じゃんって思うかもしれませんが、そんなことをするとメモリをたくさん消費することになるのでダメです。

スコープの種類

スコープにはいくつか種類があるんですが大きく分けると二つに分類されます。グローバルスコープとローカルスコープです。

グローバルスコープ

グローバルスコープはどこからでも変数や関数が使えるスコープです。ここで定義された変数はどこでも使用できます。定義の仕方は関数などには入れずに変数を宣言するか、「var,let,const」などを使わずに定義するとグローバルスコープに定義できます。

    
      var globalScp_1 = 'global_1';
      function scpFunc() {
        globalScp_2 = 'global_2';
      }
    
  

ローカルスコープ

ローカルスコープはグローバルスコープ以外の全てのスコープがこれに分類されます。
ローカルスコープの中にはさらに関数スコープやブロックスコープがあります。ありはするんですが意味がほとんど一緒です。
ブロックスコープは{}で括られたエリア内で変数や関数が使えるスコープです。
関数スコープはその名の通り関数内で変数や関数が使えるスコープで、関数の引数も関数内の変数と同じ扱いになります。なので同じ関数スコープ内でしか扱えません。
どちらも{}で囲まれていて引数があるかどうかくらいしか違いはないです。基本的に{}の中で定義された変数や関数はそのスコープでしか使えないって解釈でいいと思います。

    
      function scpFunc(demo) {
        var functionScp = 'function';
        // この中だと使えるだとfunctionScpとdemoを使える
      }
      // ここはfunctionScpとdemoを使えない
    
  

この辺でスコープの復習は終わります。いよいよ本題です。

for文の()の中は引数?

スコープの復習をした感じだとfor文の()内で定義したものは引数扱いでfor文ないでしか使えないんじゃないかと予想を立てました。結果がわからないのでとりあえず実験です。
ちなみにMDNのサイトには「初期化式」と書いてありました。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Loops_and_iteration

まずはベースのfor文です。

    
      const target = document.getElementById('js-txt');
      for(var i = 0; i<10; i++) {
        target.innerText += `scope1:${i}\n`;
      }
    
  

実行すると

    
      scope1:0
      scope1:1
      scope1:2
      scope1:3
      scope1:4
      scope1:5
      scope1:6
      scope1:7
      scope1:8
      scope1:9
    
  

では、for文のなかにfor文を入れたらどうなるかみようと思います。

    
      for(var i = 0; i<10; i++) {
        target.innerText += `scope1:${i}\n`;
        for(var i = 0; i<10; i++) {
          target.innerText += `scope1_2:${i}\n`;
        }
      }
    
  

実行すると

    
      scope1:0
      scope1_2:0
      scope1_2:1
      scope1_2:2
      scope1_2:3
      scope1_2:4
      scope1_2:5
      scope1_2:6
      scope1_2:7
      scope1_2:8
      scope1_2:9
    
  

この結果はちょっと意外でした。scope1:0も0~9まで吐き出されるかと思ったんですが最初の一回で止まっちゃいましたね。でも冷静に考えたら当たり前でiはscope1_2の方で9まで上がっているのでscope1_2のfor文が終わったら必然的にscope1も終わってしまうって訳ですね。次はfor文の外に同じ初期化式のfor文をおいた場合をみていきたいと思います。

    
      const target = document.getElementById('js-txt');
      for(var i = 0; i<10; i++) {
        target.innerText += `scope1:${i}\n`;
      }
      for(var i = 0; i<10; i++) {
        target.innerText += `scope2:${i}\n`;
      }
    
  

実行すると

    
      scope1:0
      scope1:1
      scope1:2
      scope1:3
      scope1:4
      scope1:5
      scope1:6
      scope1:7
      scope1:8
      scope1:9
      scope2:0
      scope2:1
      scope2:2
      scope2:3
      scope2:4
      scope2:5
      scope2:6
      scope2:7
      scope2:8
      scope2:9
    
  

この結果からfor文は関数スコープと同じ扱いなんだなとわかります。
同じ扱いなのがわかったのはいいんですが、for文内でさらにfor文を回す時に初期化式なんてなんでもいいって時はとりあえずvar i = 0にしてたんですがfor文の中で再度for文書くときは名前を考えた方がいいのかもしれませんね。

まとめ

今回はスコープについて改めて調べ直しました。抜けていたの引数の扱いがどうなるかってところでした。スコープについて調べている過程でletとconstがie11でも使えるって記事を見つけたので今度letとconstで値の呼び出しにどんな違いが出てくるのか実験してみたいと思います。

Scroll to top