関数宣言はどのように処理されますか?
var abc = '';
if(1 === 0){
function a(){
abc = 7;
}
}else if('a' === 'a'){
function a(){
abc = 19;
}
}else if('foo' === 'bar'){
function a(){
abc = 'foo';
}
}
a();
document.write(abc); //writes "foo" even though 'foo' !== 'bar'
この例は、ChromeとFirefoxで異なる出力を生成します。Chromeはfoo
を出力し、FFは19
。
この質問が尋ねられたとき、ECMAScript 5(ES5)が流行していました。 ES5の厳密モードでは、質問に示されているように、関数宣言をif
ブロック内にネストすることはできません。非厳密モードでは、結果は予測不能でした。異なるブラウザとエンジンは、ブロック内の関数宣言を処理する方法について独自のルールを実装しました。
2018年の時点で、多くのブラウザーは ブロック内での関数宣言が許可されている の範囲でECMAScript 2015(ES2015)をサポートしています。 ES2015環境では、ブロック内の関数宣言はそのブロック内でスコープされます。関数a
はif
ステートメントのスコープ内でのみ宣言され、したがってグローバルスコープには存在しないため、質問のコードは未定義の関数エラーになります。
関数を条件付きで定義する必要がある場合は、 関数式 を使用する必要があります。
から http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
JavaScriptでは、関数宣言があります:
_function foo() {
}
_
および関数式
_var foo = function() {
}
_
http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting からの引用
「関数の宣言と関数変数は、JavaScriptインタープリターによって常にJavaScriptスコープの最上部に移動(「引き上げ」)されます」。
最初の例で起こったことは、function a()
の関数宣言がJavascriptスコープの最上部に引き上げられ、ifがfalseと評価されても 'foo'を生成することです。
_var foo
_を通常のJavascriptステートメントと考えてください。function foo()
とは異なり、javascriptのランタイムでのみ実行されます。そのため、以下が有効です。
_alert(foo());
function foo() {
return 'gw ganteng';
}
_
ここで、function foo()
は、パーサーによって解析され、foo()
を呼び出そうとする前に、alert(foo())
を現在のスコープに入れます。
http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
JavaScriptの実行には、コンテキスト(ECMA 5がLexicalEnvironment、VariableEnvironment、およびThisBindingに分割)とプロセス(順番に呼び出される一連のステートメント)があります。実行スコープに入ると、宣言がVariableEnvironmentに寄与します。これらはステートメント(リターンなど)とは異なり、プロセスのルールの対象ではありません。
ECMA-262 v5では、新しいグローバルまたは関数レベルの実行コンテキストを入力する最初のパスで、すべての関数と変数の宣言を登録する実装が必要です。 Chromeは、else
ブロックとthen
ブロックの中を見て、実行前にa()
を登録しているため、技術的にここで実行しています。最も読みにくい結果が生成されます。
FFは、ifステートメントを評価するまで待機してから、関数と変数の宣言を評価し、現在のコンテキストに追加します。ところで。両方のブラウザーは、このようにcatch節とfinally節内でそれを行います。
実際には、本来存在するべきではない機能を扱う2つの異なるECMA実装の問題です。手元のシナリオは、関数宣言を制御フローステートメント内に含めるべきではない理由を示しています。