18

私の知る限り、JavaScriptfunction foo() { aaa(); }だけです。var foo = function(){ aaa() }したがって、追加することで変数をfunction foo() { bbb(); }上書きするかfoo、2番目の定義を無視する必要があります-それは重要ではありません。重要なのは、1つの変数が必要であるということですfoo

したがって、この例では、me変数はメソッド内から正しく解決されるべきではなく、Explorer8にはありません:-)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

デモ: http: //jsfiddle.net/W5dqy/5/

4

3 に答える 3

35

AFAIK関数foo(){aaa(); }は、JavaScriptではvar foo = function(){aaa()}です。

完全ではありません。それらはていますが、まったく異なります。JavaScriptには、2つの異なる、しかし関連するものがあります。関数宣言(最初の例)と関数(2番目の例で変数に割り当てます)です。それらは解析サイクルのさまざまな時間に発生し、さまざまな効果があります。

これは関数宣言です:

function foo() {
    // ...
}

関数宣言は、ステップバイステップのコードが実行される前に、囲んでいるスコープに入るときに処理されます。

これは関数(具体的には匿名の式)です。

var foo = function() {
    // ...
};

関数式は、(他の式と同じように)表示された時点でステップバイステップコードの一部として処理されます。

引用符で囲まれたコードは、次のような名前付き関数式を使用しています。

var x = function foo() {
    // ...
};

:(あなたの場合、それはオブジェクトリテラル内にあるので、の代わりに右側にありますが=、それでも名前付き関数式です。)

これは完全に有効であり、実装のバグを無視します(後で詳しく説明します)。、という名前の関数を作成し、それをfoo囲むスコープに入れに、その関数を変数に割り当てます(これはすべて、ステップバイステップのコードで式が検出されたときに発生します)。私がそれが囲んでいるスコープに入れないと言うとき、私はまさにそれを意味します:fooxfoo

var x = function foo() {
    alert(typeof foo); // alerts "function" (in compliant implementations)
};
alert(typeof foo);     // alerts "undefined" (in compliant implementations)

これが関数宣言の動作(関数の名前囲んでいるスコープに追加される)とどのように異なるかに注意してください。

名前付き関数式は、準拠した実装で機能します。歴史的に、実装にはバグがありました(初期のSafari、IE8以前)。IE9以降を含む最新の実装はそれらを正しくします。(詳細はこちら:ダブルテイクとこちら:名前付き関数の式はわかりやすく説明されています。)

したがって、この例では、me変数shoudlはメソッド内から正しく解決されません。

実際にはそうあるべきです。関数の実際の名前(との間の記号functionと開き括弧)は、関数内で常に範囲内にあります(関数が宣言からのものであるか、名前付き関数式からのものであるかは関係ありません)。

:以下は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。)

処理方法は次のとおりです。

  1. ステップバイステップのコードで行が検出されると、匿名の囲み関数が作成されvar foo = ...、それが呼び出されます(()最後にがあるため)。
  2. その無名関数によって作成された実行コンテキストに入ると、bar1_meおよびbar2_me関数の宣言が処理され、それらのシンボルがその無名関数内のスコープに追加されます(技術的には実行コンテキストの変数オブジェクトに追加されます)。
  3. シンボルは無名関数内のpublicSymbolsスコープに追加されます。(詳細:誤解が少ないvar
  4. ステップバイステップのコードは、に割り当てること{}から始まりますpublicSymbols
  5. ステップバイステップのコードは、publicSymbols.bar1 = bar1_me;publicSymbols.bar2 = bar2_me;、そして最後に続きますreturn publicSymbols;
  6. 匿名関数の結果はに割り当てられ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の場合は同じ行で)、大きな構造のメンテナンスが簡単になるため、通常はそのようにはしません。 。

于 2011-02-28T13:03:30.600 に答える
3

両方のルックアップは、関数式meでのみ表示/使用可能です。

実際、これら2つは名前付き関数式であり、ECMAscript仕様では、式の名前はそのような呼ばれるものに公開されていないことが示されていVariable objectます。


さて、私はそれをほんの少しの言葉で表現しようとしましたが、正しい言葉を見つけようとしている間、これはECMAscriptの振る舞いのかなり深い連鎖に終わります。したがって、/には格納されfunction expressionませ。(それらの人が誰であるかという質問につながるでしょう...)。VariableActivation Object

短い:関数が呼び出されるたびに、新しい関数Contextが作成されます。Activation objectいくつかのものを保存する、と呼ばれる「blackmagic」のような人がいます。たとえば、

  • 関数の引数
  • スコープ]]
  • によって作成された変数var

例えば:

function foo(test, bar) {
    var hello = "world";

    function visible() {
    }

    (function ghost() {
    }());
}

のアクティベーションオブジェクトは次のfooようになります。

  • 引数:テスト、バー
  • 変数:hello(文字列)、visible(関数)
  • [[スコープ]]:(可能な親関数-コンテキスト)、グローバルオブジェクト

ghostAOには保存されません!関数自体のでその名前でアクセスできるようになります。は関数宣言(または関数ステートメントvisible())ですが、AOに格納されます。これは、構文解析時に関数宣言が評価され、実行時に関数式が評価されるためです。

于 2011-02-28T13:05:43.823 に答える
1

ここで起こることは、それfunction()が多くの異なる意味と使用法を持っているということです。

私が言ったら

bar1 : function me() {
}

それは100%同等です

bar1 : function() {
}

つまり、関数を使用して変数を割り当てる場合、名前は重要ではありませんbar1。内部では、meが割り当てられますが、関数定義が残されるとすぐに(を割り当てるとbar2)、にme格納されている関数定義のローカル変数として再度作成されbar2ます。

于 2011-02-28T13:06:14.307 に答える