Benn Flynn のコメントに沿って、パーサーは実際に実行される前にコードを事前スキャンおよび/またはコンパイルします。したがって、ブロック内の実行されているかどうかに関係なく、任意の行から変数のスコープを検出します。
ブラウザのコンソールでこれを実行します。
var f = function() {
// we haven't defined a local x yet, so we're tempted to think
// that this line is mucking with window.x
x = 1;
console.log(x, window.x);
return;
// and we're tempted to think this line is never seen, because it
// occurs after the return. but, it ensures that x is local, not global.
var x = 2;
};
f();
コンソールに次のように表示されます。
1 undefined
そしてその後、window.x
存在しません。私たちの宣言のおかげで、どこにも言及されていません。宣言の代入部分が必要かどうかはわかりません。そうであってはなりません。しかし、割り当ても存在しない場合、IE の一部のバージョンが宣言を無視することを知っても驚かないでしょう。
これと比較してください:
var f = function() {
x = 1;
console.log(x, window.x);
return;
x = 2; // this line truly does nothing.
};
f();
コンソールに次のように表示されます。
1 1
そしてその後、windows.x
です1
。
definedAfter()
変数代入形式で書かれていないため、正しく動作することも注目に値します。
// this puts definedAfter in scope, but it's undefined unless this line is
// actually executed
var definedAfter = function() { /* whatever */ }
代わりに、「実行」されない「関数宣言」形式で記述されています。
function definedAfter() { /* whatever */ }
むしろ、コンパイラはコンパイル中にそれを見て、「ああ、この関数はスコープの一部です」と言います。この違いにより、関数をいつ使用できるかを変数代入構文で正確に制御できるようになります。関数宣言形式では、実行順序を気にせずにどこでも関数を定義できます。