JavaScriptのスコープ

サイ本を読みながらJavaScriptの勉強中ですが、その中でJavaScriptのスコープがちょっと特殊だったんで書いときます。

グローバルとローカル

まずは基本的なところで、グローバル変数として定義した場合とfunction()内にローカル変数として定義した場合のスコープ

var scope = "global";     // グローバル変数を宣言する。
function checkscope(){
  var scope = "local";    // 同じ名前のローカル変数を宣言する。
  alert(scope);           // グローバル変数ではなく、ローカル変数が使われる。
}
checkscope();             // localが表示される。
alert(scope);             // globalが表示される。

これは、CやJavaとかでも一緒なんで違和感とかないと思います。
ただ、varを使って宣言しなかった変数にいきなり代入とかをするとグローバル変数になってしまうので注意してください。

function checkscope(){
  myscope = "local";      // ローカル変数のように見えるが、
                          // varをつけていないためグローバル変数として扱われる。
  alert(local_scope);
}
checkscope();             // localが表示される。
alert(myscope);           // localが表示される。

ブロックレベルのスコープ

CやJavaと違って、JavaScriptにはブロックレベルのスコープがありません。
これがJavaScript特有のもので、個人的には結構驚きでした。
これは、ある関数内で宣言された変数はブロック内であろうがなかろうが、その関数内すべてで使えると言うことです。
つまり、if文やfor文の中で宣言された変数はそのブロックやループの外でも利用できると言うことです。

function test(o){
  var i = 0;                  // iはこの関数全体で有効。
  if(typeof o == "object"){
    var j = 0;                // jはこのブロックだけでなく、関数全体で有効。
    for(var k=0; k<10; k++){  // kはこのループだけでなく、関数全体で有効。
      alert(k);
    }
    alert(k);                 // kは有効。「10」が表示される。
  }
  alert(j);                   // jは定義されたが、初期化されていない場合もある。
}

また、ある関数で宣言されたすべての変数はその関数全体で有効である、というルールによって起こる不思議な現象ってのが、一番驚きました。それと同時に納得もしましたけど。
それは以下に示すようなことです。

var scope = "global";
function f(){
  alert(scope);        // 「global」ではなく「undefined」が表示される。
  var scope = "local"  // ここで初期値が設定された。宣言は関数全体で有効。
  alert(scope);        // 「local」が表示される。
}
f();

正直意味わかりませんよねw
何で最初のalertはglobalじゃないの?って感じだと思います。
これは関数内のどこでローカル変数を宣言しても、その宣言は関数全体で有効だからです。
つまり、ローカル変数の宣言が関数内の最初だろうが最後だろうが途中だろうが一緒ということです。
なので、上の関数f()は以下に書くのと同じになります。

var scope = "global";
function f(){
  var scope;           // 関数の先頭でローカル変数を宣言する。
  alert(scope);        // 変数は存在するが、未定義値の状態。
  var scope = "local"  // 初期値を設定した。
  alert(scope);        // これで未定義値ではなくなる。
}
f();

ということです。
こういうことなので、変数の宣言は関数の先頭でまとめて記述したほうが、コードが読みやすくなり、バグも減らせると思います。