web-dev-qa-db-ja.com

JavaScript:インライン関数と事前定義関数

事前定義関数 nameをハンドラーに渡すことに対して、インライン関数を使用するために、任意のボディからいくつかの引数をスローできますか?.

つまりより良い:

(function() {
  setTimeout(function() { /*some code here*/ }, 5);
})();

versus

(function() {
  function invokeMe() {
    /*code*/
  }
  setTimeout(invokeMe, 5);
})();


奇妙な質問ですが、私たちはこれについてほとんどチームで戦っています。

44
glaz666

名前付き関数

このページの質問と回答には、用語の重大な誤用があります。関数がインライン(関数式)であるかどうかは、名前を付けることができないということではありません。

これは、関数式を使用しています

setTimeout(function doSomethingLater() { alert('In a named function.'); }, 5);

そして、これはfunctionステートメントを使用しています:

function doSomethingLater() { alert('In a named function.'); }
setTimeout(doSomethingLater, 5);

どちらの例も名前付き関数を使用しており、ツールのデバッグとプロファイリングに関しては両方とも同じ利点があります!

名前が指定されている場合(「関数」の後、括弧の前のテキスト)、インラインまたは個別に宣言されているかどうかに関係なく、名前付き関数になります。名前が指定されていない場合、「匿名」です。

注:T.J. IE自明ではない方法で名前付き関数式を誤って処理する(参照: http://kangax.github.com/nfe/#jscript-bugs )これは注意することが重要です、私は単に用語について指摘しようとしているだけです。

どちらを使用すべきですか?

直接の質問に答えて、コード内の他の場所から関数を使用できる可能性がある場合は、名前付き関数ステートメントを使用する必要があります。関数が1つの場所でのみ使用されており、他のどこにも関連性がない場合、(式の理由で)極端に長くなったり、場違いを感じない限り、関数式を使用します。インライン関数式を使用する場合、デバッグやコードの明確化のために、とにかく名前を付けると便利です。

メモリリーク

関数に名前を付けるか、関数ステートメントを使用するか、関数式を使用するかは、メモリリークの問題にほとんど影響しません。これらのリークの原因を説明してみましょう。このコードを見てください:

(function outerFunction() {
    var A = 'some variable';

   doStuff();
})();

上記のコードでは、「outerFunction」が終了すると「A」が範囲外になり、ガベージコレクションが行われ、メモリが解放されます。

そこに関数を追加するとどうなりますか?

(function outerFunction() {
    var A = 'some variable';

   setTimeout(function(){ alert('I have access to A whether I use it or not'); }, 5);
})();

このコード(上記)で、setTimeoutに渡す関数式には「A」への参照があり(クロージャの魔法による)、「outerFunction」が終了した後に「A」が残りますタイムアウトがトリガーされ、関数が逆参照されるまでメモリを使用します

その関数をsetTimeout以外のものに渡すとどうなりますか?

(function outerFunction() {
    var A = 'some variable';

   doStuff(function(){ alert('I have access to A whether I use it or not'); });
})();

function doStuff(fn) {
    someElement.onclick = fn;
}

これで、「doStuff」に渡す関数式は「A」にアクセスでき、「outerFunction」が終了した後でも「A」への参照がある限り、メモリに残ります。 doStuffに渡した関数。この場合、その関数への参照を(イベントハンドラとして)作成しているため、そのイベントハンドラがクリアされるまで「A」はメモリに残ります。 (例:誰かがsomeElement.onclick = null

関数ステートメントを使用するとどうなるか見てみましょう。

(function outerFunction() {
    var A = 'some variable';

    function myFunction() { alert('I have also have access to A'); };
    doStuff(myFunction);
})();

同じ問題! 「doStuff」が参照を保持していない場合にのみ「myFunction」がクリーンアップされ、「myFunction」がクリーンアップされた場合にのみ「A」がクリーンアップされます。ステートメントを使用したか式を使用したかは関係ありません。重要なのは、その関数への参照が「doStuff」で作成されている場合です!

52
Prestaul

2つの間にoneの大きな違いがあります。後者には名前があります。

私は自分のツールが私を助けるのを手伝いたいので、ほとんどの場合 匿名関数を避ける 私のツールはそれらについて意味のある情報を提供できないので(例えば、デバッガーの呼び出しスタックリストなどで) )。だから私は

(function(){
  function invokeMe() {
    /*code*/
  }
  setTimeout(invokeMe, 5);
})();

...一般的なフォーム。ただし、規則は破られることを意図しており、奴隷に屈することはありません。 :-)

仕様によると、3番目の選択肢があることに注意してください。名前も持つインライン関数を使用できます。

(function(){
  setTimeout(function invokeMe(){ /*some code here*/ }, 5);
})();

ただし、問題は、これまでのMicrosoftのJavaScriptインタープリターのすべてのバージョン(「JScript」)(IE9のバージョンを含む)が、という名前の関数式を誤って処理することです。そして、two異なる時間に完全に異なる関数を作成します。 ( Proof 、IE9以前で試してみてください。他のブラウザでも試してください。)IEは2つの方法で間違っています。オブジェクト、および2.これらのいずれかの結果として、名前シンボルを式の囲みスコープに「ブリード」します( セクション1 仕様の明確な違反)。ここで詳細:- ダブルテイク

11
T.J. Crowder

IMO、関数の宣言は、後で他の方法で再利用する場合にのみ役立ちます。

私はsetTimeoutハンドラーに関数式(最初の方法)を個人的に使用します。

ただし、関数宣言と関数式の違いの違いを知りたい場合は、次の記事をお勧めします。

5
CMS

そのような議論を解決するために、対立するチームメンバー間の完全な決闘を提案します。

より深刻なことは、最終的には問題ではありません。最初の形式(名前のない関数)は、より大きな関数では扱いにくい傾向がありますが、小さな(1-2行)関数では大したことではありません。 2番目の形式も同様に無害です。

どちらのスタイルに対する引数も純粋です bikeshedding 、imo。

3
jsight

インライン関数は名前空間の汚染を回避し、事前定義された関数は再利用率が高くなります。それぞれが適切なケースを作成できると思います。

2
Mark

みんな仲良くできないの?

(function(){
  setTimeout( (function InvokeMe(){ /*some code here*/ }), 5);
})();

本当に重要なのはIMOだけで、それがデバッグの容易さです。多くのステップトレーサーは、関数が匿名で引数を持っているという事実以外はfuncについて何も伝えることができませんが、評価を強制するために定義を括弧に入れることで名前でインラインを定義できます。非常に単純なまたは明らかな破壊機能については、それは大したことではないと思いますが、私にとっては準決勝のようなものです。他の人が痛みを引き起こさないのなら、私は他の人が何をするかは本当に気にしません。

1
Erik Reppen

定義済みの名前付き関数は、 http://callbackhell.com/ で言及されているJavaScriptコールバックの地獄の問題を軽減できます。

1
Alireza Fattahi

これは古い質問であることは知っていますが、私にとっては、すでに述べたものよりもさらに重要な違いがあります。ホイストすべての関数を作成する必要があるため、メモリ内の領域を予約し、後でGCにする必要があります。

名前付き関数は、周囲の関数の先頭に引き上げられるため、使用されるかどうかにかかわらず、すべての関数呼び出しで作成されます。無名関数は、それらを定義するコードが実行された場合にのみ作成されます。

//an example where you wold prefer to use an anonymous function.
//you can assign this (anonymous) function to a variable, so you get your "name" back.
function someFn(){
    if(condition){
        //the variable declaration has been hoisted, 
        //but the function is created at this point, and only if necessary.
        var process = function(value){/* */};
        switch(condition2){
            case 1: process(valueFor1); break;
            case 2: process(valueFor2); break;
            /* ... */
        }
    }
}

function someFn(){
    var process;
    if(condition){
        process = function(value){ /* A */ }
    }else{
        process = function(value){ /* B */ }
    }

    //beware, depending on your code, "process" may be undefined or not a function
    process(someValue);
}


//an example where you would prefer (/ utilize) the hoisting.
function someFn(){
    /* some code */
    while(condition){
        //some might want to keep the function definition near the code where it is used,
        //but unlike an anonymous function or a lambda-expression this process-function 
        //is created only once per function-call, not once per iteration.
        function process(value, index){ /* ... */ }
        /* ... */
        process(value, index)
    }
}

だから、経験則として:

  • ループ内では、匿名関数またはラムダ式は使用できません。

  • (まれにtrue)条件内でのみ関数が必要な場合は、必要な場合にのみ作成されるため、名前付き関数よりも匿名関数を優先する必要があります。

  • 自分のビジネス(JavaScript)を知っている場合、このアドバイスを無視するタイミングを知っている

1
Thomas

そのようなコードの唯一の違いは、2番目のコードで同じ関数を再呼び出しできることだと思います(「タイマー関数」を使用すると便利な場合があります)。

(function(){
  function invokeMe() {
    if(..) setTimeout(invokeMe, 5);
  }
  setTimeout(invokeMe, 5);
})();
1
mck89

私も名前付き関数に向かう傾向があります。無名関数の参照は高速ですが、単純なものにのみ使用する必要があります。私の経験則では、関数が2行を超えるコードである場合、おそらくそれは独自の定義に属します。

これは、匿名関数を使用するほとんどのサンプルコードでは複雑です。しかし、サンプルは通常非常に単純です。方法が複雑になると、メソッドはバラバラになります。開発者が後続の手順でさらにコールバックが必要であることに気付いたときに、関数参照にネストされた関数参照を見てきました。このツリーベースのロジックの代わりに、孤立した機能の編成を好みます。

そして通常、後で定義する関数の1つを再利用できることに満足します。

無名関数の重要な使用法の1つは、スコープデータを関数呼び出しに渡す必要がある場合ですが、通常は関数を匿名関数にラップするだけです。

また、テスト駆動開発を開始する場合は、名前付き関数も絶対に必要です。

0
Ray Wadkins

提供されている例では、関数の宣言と使用が非常に近いため、唯一の違いは読みやすさです。私は2番目の例を好む。

0
lincolnk

あるバージョンを他のバージョンよりも優先する技術的な理由はありません。私にとっては、通常2つのことに依存しています。

  1. 渡されたコールバックを別のコンテキストで再利用したいです。この場合、関数をスタンドアロンで定義し、参照を渡します。
  2. コールバックは最大10行のコードよりも大きく、関数はコールバック後に追加の引数を必要とします。この場合、どの値が実際に関数に渡されるかを再構築するのは困難です。

例:

setTimeout(function() { // I need to scroll to see the other arguments

  // many lines of code

}, 0); // <- where does this '0' belong to?
0
Fabian Jakobs

名前付き関数を使用することを好みます。名前付き関数は、すべてのデバッガー(air、firebug、IE)で名前で表示されます。

例:

次のようなインラインの名前付き関数を使用することもできます。

{
    method: function obj_method(){}
}

このように、スタックトレースを見ると、匿名ではなく関数obj_methodが表示されます。

関数を宣言するのではなく、いつインライン化するかを尋ねましたか?コードで意味がある場合。 2つの異なる場所から必要な場合、インラインにすることはできません。場合によっては、インラインでコードを読みやすくすることもあれば、難しくすることもあります。

0
Juan Mendes