これはスコープの問題でも、クロージャの問題でもありません。問題は、宣言と式の間の理解にあります。
JavaScriptコードは、NetscapeのJavaScriptの最初のバージョンとMicrosoftの最初のコピーでさえ、2つのフェーズで処理されます。
フェーズ1:コンパイル-このフェーズでは、コードは構文ツリー(およびエンジンに応じてバイトコードまたはバイナリ)にコンパイルされます。
フェーズ2:実行-解析されたコードが解釈されます。
関数宣言の構文は次のとおりです。
function name (arguments) {code}
引数はもちろんオプションです(コードもオプションですが、そのポイントは何ですか?)。
ただし、JavaScriptでは、式を使用して関数を作成することもできます。関数式の構文は、式のコンテキストで記述されることを除いて、関数宣言に似ています。そして、式は次のとおりです。
=
記号の右側(または:
オブジェクトリテラル上)にあるもの。
- 括弧内はすべて
()
。
- 関数へのパラメーター(これは実際にはすでに2でカバーされています)。
宣言とは異なり、式はコンパイルフェーズではなく実行フェーズで処理されます。このため、表現の順序が重要になります。
したがって、明確にするために:
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();
フェーズ1:コンパイル。コンパイラーは、変数someFunction
が定義されていることを確認して作成します。デフォルトでは、作成されるすべての変数の値はundefinedです。値は、割り当てる値を返すために何らかのコードを実行するためにインタープリターを必要とする可能性があるため、コンパイラーはこの時点ではまだ値を割り当てることができないことに注意してください。そしてこの段階では、まだコードを実行していません。
フェーズ2:実行。インタプリタは、変数someFunction
をsetTimeoutに渡したいことを認識します。そしてそうです。残念ながら、の現在の値someFunction
は未定義です。
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();
フェーズ1:コンパイル。コンパイラーは、someFunctionという名前の関数を宣言していることを認識し、それを作成します。
フェーズ2:インタープリターはsomeFunction
、setTimeoutに渡す必要があることを確認します。そしてそうです。の現在の値someFunction
は、コンパイルされた関数宣言です。
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();
フェーズ1:コンパイル。コンパイラは、変数を宣言したことを確認しますsomeFunction
作成します。以前と同様に、その値は未定義です。
フェーズ2:実行。インタプリタは、後で実行されるように無名関数をsetTimeoutに渡します。この関数では、変数を使用していることがsomeFunction
わかるため、変数のクロージャが作成されます。この時点では、の値someFunction
はまだ定義されていません。次に、に関数を割り当てていることがわかりますsomeFunction
。この時点で、の値はsomeFunction
未定義ではなくなりました。100分の1秒後に、setTimeoutがトリガーされ、someFunctionが呼び出されます。その値はもはや未定義ではないため、機能します。
ケース4は、実際にはケース2の別のバージョンであり、ケース3のビットがスローされsomeFunction
ています。setTimeoutに渡される時点で、宣言されているため、すでに存在しています。
追加の説明:
setTimeout(someFunction, 10)
someFunctionのローカルコピーとsetTimeoutに渡されたコピーの間にクロージャを作成しないのはなぜか疑問に思うかもしれません。その答えは、JavaScriptで関数の引数は、常にあるということです、常に彼らは数字や文字列や他のすべてのために参照することによってである場合に値渡し。したがって、setTimeoutは、実際には変数someFunctionが渡されるのではなく(クロージャが作成されることを意味します)、someFunctionが参照するオブジェクト(この場合は関数)のみを取得します。これは、クロージャを解除するためにJavaScriptで最も広く使用されているメカニズムです(ループなど)。