[解決済み] JavaScriptの関数宣言と評価順序
質問
これらの例のうち、最初のものは動作しませんが、他のものはすべて動作するのはなぜですか?
// 1 - does not work
(function() {
setTimeout(someFunction1, 10);
var someFunction1 = function() { alert('here1'); };
})();
// 2
(function() {
setTimeout(someFunction2, 10);
function someFunction2() { alert('here2'); }
})();
// 3
(function() {
setTimeout(function() { someFunction3(); }, 10);
var someFunction3 = function() { alert('here3'); };
})();
// 4
(function() {
setTimeout(function() { someFunction4(); }, 10);
function someFunction4() { alert('here4'); }
})();
どのように解決するのですか?
これはスコープの問題でもクロージャーの問題でもありません。問題は 宣言 と 式 .
JavaScriptのコードは、Netscapeの最初のバージョンやMicrosoftの最初のコピーでさえ、2つのフェーズで処理されます。
フェーズ 1: コンパイル - このフェーズでは、コードは構文木 (およびエンジンに依存するバイトコードまたはバイナリ) にコンパイルされます。
フェーズ 2: 実行 - パースされたコードは次に解釈されます。
関数の構文 宣言 は
function name (arguments) {code}
引数はもちろん任意です(コードも任意ですが、何の意味があるのでしょうか?)
しかし、JavaScriptでは 式 . 関数式の構文は、式コンテキストで書かれることを除けば、関数宣言に似ています。そして、式は
-
の右にあるものはすべて
=
記号(または:
を使用することができます。) -
括弧の中にあるもの
()
. - 関数へのパラメータ (これは実際にはすでに 2 でカバーされています)。
式 とは異なり 宣言 はコンパイルフェーズではなく実行フェーズで処理されます。そしてこのため、式の順序は重要です。
そこで、明確にするために
// 1
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();
フェーズ1:コンパイル。コンパイラは、変数
someFunction
が定義されていることを確認し、それを作成します。デフォルトでは、作成されたすべての変数の値はundefinedである。コンパイラはこの時点ではまだ値を代入できないことに注意してください。なぜなら、代入する値を返すために、インタプリタが何らかのコードを実行する必要があるかもしれないからです。そして、この段階ではまだコードは実行されていません。
第2段階:実行 インタプリタは、あなたが変数を渡したいことを見て
someFunction
を setTimeout に渡したいことがわかります。そして、そうします。残念ながら現在の
someFunction
の現在の値は未定義です。
// 2
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();
フェーズ1:コンパイル。コンパイラはあなたがsomeFunctionという名前の関数を宣言していることを見て、それを作成します。
フェーズ2: インタプリタは、あなたが
someFunction
を setTimeout に渡したいことがわかります。そして、そうします。の現在の値は
someFunction
はそのコンパイルされた関数宣言です。
// 3
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();
フェーズ1:コンパイル。コンパイラは、あなたが変数を宣言したことを認識します。
someFunction
を宣言し、それを生成します。先ほどと同様に、その値は未定義です。
フェーズ2:実行 インタープリタは、後で実行される無名関数をsetTimeoutに渡します。この関数では、あなたが変数
someFunction
という変数を使っていることがわかるので、その変数に対するクロージャを作成します。この時点で
someFunction
の値はまだ未定義です。次に、あなたが関数を
someFunction
. この時点で
someFunction
の値はもはや未定義ではない。1/100秒後にsetTimeoutがトリガーされ、someFunctionが呼び出される。その値はもはや未定義ではないので、それは動作します。
ケース4はケース2の別バージョンで、ケース3も少し含まれています。ポイントでは
someFunction
が setTimeout に渡された時点で、それが宣言されているため、すでに存在しています。
追加の説明。
あなたは、なぜ
setTimeout(someFunction, 10)
が someFunction のローカルコピーと setTimeout に渡されるものとの間にクロージャを作らないのか不思議に思うかもしれません。その答えは、JavaScriptにおける関数の引数は常に
常に
数値や文字列の場合は値で、それ以外の場合は参照で渡されます。つまり、setTimeout は実際に渡された変数 someFunction を取得するのではなく (これはクロージャの作成を意味します)、むしろ someFunction が参照するオブジェクト (この場合は関数です) を取得するだけなのです。これは JavaScript でクロージャを解除するために最も広く使われているメカニズムです (たとえばループの中で)。
関連
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] なぜGoogleはJSONレスポンスにwhile(1);を前置するのでしょうか?
-
[解決済み] event.preventDefault() vs. return false
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
-
[解決済み] jqueryでdivの要素がオーバーフローしていないかチェックする
-
[解決済み] なぜ "use strict "はパフォーマンスを10倍向上させるのか?
-
[解決済み] JavaScript のオブジェクトの配列を比較し、最小値/最大値を取得する
-
[解決済み] V8 Javascript エンジンのスタンドアロン実行
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] javascriptで2つの数値を連結する方法は?
-
[解決済み] 文字列がすべて同じ部分文字列で構成されているかどうかを調べるにはどうすればよいですか?
-
[解決済み] JavaScriptでの大文字小文字を区別しない正規表現
-
[解決済み] jQueryの$という記号の意味は何ですか?
-
[解決済み] moment.jsでミュータビリティを回避するには?
-
[解決済み] jqueryはjavascriptのライブラリなのかフレームワークなのか?[クローズド]
-
[解決済み] CORS: 認証モードは 'include' です。
-
[解決済み] javascriptでオプションのパラメータを扱う
-
[解決済み] jQueryを使用して、すべてのクリックイベントハンドラを削除するにはどうすればよいですか?
-
[解決済み] 変異を伴わないオブジェクトからの値の削除