私の知る限り、JavaScriptではfunction foo() { aaa(); }
は単なるvar foo = function(){ aaa() }
です。したがって、function foo() { bbb(); }
を追加すると、foo
変数が上書きされるか、2番目の定義が無視されます。これは重要ではありません。重要なのは、1つの変数foo
が必要であるということです。
したがって、この例では、me
変数はnotメソッド内から正しく解決される必要がありますそしてExplorer 8にはありません:-)。 (var
)me
が存在する別のクロージャーにそれらをラップしようとしてこの例に到達しましたが、それが必要ではないことに驚きました。
var foo = {
bar1 : function me() {
var index = 1;
alert(me);
},
bar2 : function me() {
var index = 2;
alert(me);
}
};
foo.bar1(); // Shows the first one
foo.bar2(); // Shows the second one
AFAIK関数foo(){aaa(); }は、JavaScriptではvar foo = function(){aaa()}です。
それは実際には正しくありません。 JavaScriptには、2つの異なる、しかし関連するものがあります。関数宣言と関数式です。それらは解析サイクルのさまざまな時間に発生し、さまざまな効果があります。
これは関数です宣言:
function foo() {
// ...
}
関数宣言は、ステップバイステップのコードが実行される前に、囲んでいるスコープに入るときに処理されます。
これは関数です式(具体的には匿名のもの):
var foo = function() {
// ...
};
関数式は、(他の式と同じように)表示された時点でステップバイステップコードの一部として処理されます。
引用されたコードは名前付き関数式を使用しています。これは次のようになります。
var x = function foo() {
// ...
};
(あなたの場合、それはオブジェクトリテラル内にあるので、:
ではなく=
の右側にありますが、それでも名前付き関数式です。)
これは完全に有効であり、実装のバグを無視します(後で詳しく説明します)。 foo
という名前の関数を作成しますしないfoo
を囲んでいるスコープに入れ、その関数をx
変数に割り当てます(これはすべて、ステップバイで式が検出されたときに発生します) -ステップコード)。 foo
を囲んでいるスコープに入れないと言うとき、私はまさにそれを意味します:
var x = function foo() {
alert(typeof foo); // alerts "function" (in compliant implementations)
};
alert(typeof foo); // alerts "undefined" (in compliant implementations)
これが関数宣言の動作方法とどのように異なるかに注意してください(関数の名前がを囲むスコープに追加されます)。
名前付き関数式は、準拠した実装で機能します。歴史的に、実装にはバグがありました(初期のSafari、IE8以前)。 IE9以降を含む最新の実装はそれらを正しくします。 (詳細はこちら: ダブルテイク そしてこちら: 名前付き関数式の謎を解き明かす 。)
したがって、この例では、
me
変数はメソッド内から正しく解決されない必要があります
実際にはそうあるべきです。関数の実際の名前(function
と開き括弧の間の記号)は、関数内で常に範囲内にあります(関数が宣言からのものか名前付き関数式からのものかは関係ありません)。
[〜#〜] note [〜#〜]:以下は2011年に書かれたものです。それ以降のJavaScriptの進歩により、私はもはや必要性を感じなくなりました。 IE8(最近は非常にまれです)を扱うつもりでない限り、以下のようなことをします。
実装のバグのため、私は名前付き関数式を避けていました。あなたの例では、me
名を削除するだけでそれを行うことができますが、 名前付き関数が好きです なので、その価値のために、これが私があなたのオブジェクトを書くために使用した方法です:
var foo = (function(){ var publicSymbols = {}; publicSymbols.bar1 = bar1_me; function bar1_me() { var index = 1; alert(bar1_me); } publicSymbols.bar2 = bar2_me; function bar2_me() { var index = 2; alert(bar2_me); } return publicSymbols; })();
(おそらくpublicSymbols
よりも短い名前を使用することを除いて。)
処理方法は次のとおりです。
var foo = ...
行が検出されると、匿名の囲み関数が作成され、呼び出されます(最後に()
があるため)。bar1_me
およびbar2_me
関数宣言が処理され、それらのシンボルがその無名関数内のスコープ(技術的には変数)に追加されます。オブジェクト実行コンテキストの場合)。publicSymbols
シンボルが無名関数内のスコープに追加されます。 (詳細: 誤解が少ないvar
){}
をpublicSymbols
に割り当てることから始まります。publicSymbols.bar1 = bar1_me;
とpublicSymbols.bar2 = bar2_me;
、最後にreturn publicSymbols;
で続きます。foo
に割り当てられます。しかし、最近では、コードを記述していない限り、IE8をサポートする必要があることがわかっています(残念ながら、2015年11月にこれを記述したとき、まだ かなりの世界市場シェア ですが、幸いにもそのシェアは急落しています)私はそれについて心配しません。最新のJavaScriptエンジンはすべて、それらを正しく理解しています。
次のように書くこともできます。
var foo = (function(){ return { bar1: bar1_me, bar2: bar2_me }; function bar1_me() { var index = 1; alert(bar1_me); } function bar2_me() { var index = 2; alert(bar2_me); } })();
...これらは関数宣言であるため、引き上げられます。宣言とプロパティへの割り当てを並べて行うと(IE8用に記述していない場合は同じ行で)、大きな構造のメンテナンスが簡単になるため、通常はそのようにはしません。 。
両方のme
ルックアップは、表示/使用可能のみです内部関数式。
実際、これら2つは名前付き関数式であり、ECMAscript仕様では、式の名前はVariable object
と呼ばれるものに公開されていないことが示されています。
さて、私はそれをほんの数語で表現しようとしましたが、正しい単語を見つけようとしている間、これはECMAscriptの動作のかなり深い連鎖に終わります。したがって、function expression
はnotVariable
/Activation Object
に格納されます。 (それらの人が誰であるかという質問につながるでしょう...)。
短い:関数が呼び出されるたびに、新しいContext
が作成されます。 Activation object
と呼ばれる「blackmagic」のような人がいて、いくつかのものを保存しています。たとえば、
var
によって作成された変数例えば:
function foo(test, bar) {
var hello = "world";
function visible() {
}
(function ghost() {
}());
}
foo
のアクティベーションオブジェクトは次のようになります。
ghost
はAOに保存されません!その名前でアクセスできるようになりますwithin関数自体。 visible()
は関数宣言(または関数ステートメント)AOに保存されます。これは、関数宣言が解析および関数式は実行時に評価されます。
ここで起こることは、function()
には多くの異なる意味と使用法があるということです。
私が言ったら
bar1 : function me() {
}
それは100%同等です
bar1 : function() {
}
つまり、関数を使用して変数bar1
を割り当てる場合、名前は重要ではありません。内部では、me
が割り当てられますが、関数定義が残されるとすぐに(bar2
を割り当てると)、格納されている関数定義のローカル変数としてme
が再度作成されます。 bar2
で。