web-dev-qa-db-ja.com

JavaScriptで同じ名前の2つの関数-これはどのように機能しますか?

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

したがって、この例では、me変数はnotメソッド内から正しく解決される必要がありますそしてExplorer 8にはありません:-)。 (varmeが存在する別のクロージャーにそれらをラップしようとしてこの例に到達しましたが、それが必要ではないことに驚きました。

    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/

17
tillda

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よりも短い名前を使用することを除いて。)

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

  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用に記述していない場合は同じ行で)、大きな構造のメンテナンスが簡単になるため、通常はそのようにはしません。 。

33
T.J. Crowder

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

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


さて、私はそれをほんの数語で表現しようとしましたが、正しい単語を見つけようとしている間、これはECMAscriptの動作のかなり深い連鎖に終わります。したがって、function expressionnotVariable/Activation Objectに格納されます。 (それらの人が誰であるかという質問につながるでしょう...)。

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

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

例えば:

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

    function visible() {
    }

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

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

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

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

3
jAndy

ここで起こることは、function()には多くの異なる意味と使用法があるということです。

私が言ったら

bar1 : function me() {
}

それは100%同等です

bar1 : function() {
}

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

1
Aaron Digulla