2017-01-18 17 views
7

私は絶対初心者です、そして、私はちょうどこれをで読んでいますJavaScript:良い部品JavaScriptでループ内に関数を作成することをお勧めしないのはなぜですか?

スコープについて説明している章では、「」と記載されています。の問題を回避するために、内部関数は外部関数の実際の変数にアクセスできます。そして、次の2つの例は、次のようになります。

//悪い例

var add_the_handlers = function (nodes) { 
    var i; 
    for (i = 0; i < nodes.length; i += 1) { 
     nodes[i].onclick = function (e) { 
      alert(i); 
     }; 
    } 
}; 

// END悪い例

var add_the_handlers = function (nodes) { 
    var helper = function (i) { 
     return function (e) { 
      alert(i); 
     }; 
    }; 
    var i; 
    for (i = 0; i < nodes.length; i += 1) { 
     modes[i].onclick = helper(i); 
    } 
}; 

著者によると、それはdoesnのため、2番目の例が優れています関数内でループを使用しないと、計算上無駄になる可能性があります。しかし、私は喪失しており、彼らと何をするべきか分かりません。彼の理論を実際の応用にどのように置くのですか?誰もがHTMLを組み合わせたこれら2つの例を説明できますか?

+2

イベントが発生したときにそれを理解することが重要です....「私はあなたが望むものではありません」http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical -example – charlietfl

+0

「2つの例がHTMLを組み合わせる」とはどういう意味ですか? –

答えて

0

この例では、多くのイベントハンドラが作成されています。イベントごとに1つ。良い例は、単一のイベントハンドラを作成し、それをすべてのイベントに割り当てます。

悪い例では、1つではなく、たくさんの別々の関数を作成しています。これは余分なオーバーヘッドと多くの潜在的なスコープの問題になる可能性があります。これには、ループ内の最後のアイテムのイベントだけを起動するなどのクロージャの問題が含まれます。

また、元の関数ポインタにアクセスできるため、良い例ではイベントをより簡単に購読解除できます。

良い例は、読んで理解しやすいだけです。ループは、要素の作成とイベントのバインドにのみ使用されます。これらのイベントの処理は他の場所で行われます。

+2

また、通常、「なぜ私のイベントは、最後の値iのために発火するのですか」というクロージャの問題にぶつかります。 –

+0

ありがとう、私は答えにそれを含めました。 – Soviut

+2

私はそうは思わない。良い例では、ノード要素ごとに独立した関数を作成し、 'helper'の中でクロージャーで' i'を取り込み、返された関数を 'onclick'属性値としてアタッチします。 'helper'は' addEventListener'で追加されたイベントリスナーを削除することはできません。 – traktor53

6

問題は閉鎖です。内部関数は、これらの関数の外で定義された変数iにアクセスできます。ループのすべての反復が実行された後、変数inodes.lengthの値を保持します。したがって、nodes[0]をクリックすると、警告はnodes.lengthと表示されますが、それはあなたが期待するものではありません。 (あなたは0という警告を期待します)nodes[1]nodes[2]などをクリックすると同じことが言えます。すべての警告はnodes.lengthと表示されます。

+0

実際、ループ変数の最終値は条件が失敗した最初の値であり、最後の値ではないため、警告に表示されるiの値は 'nodes.length - 1'ではなく' nodes.length'になりますその条件が合格する。これは確かにCrockfordが話していたものです。もしあなたが "悪いコード"を実行し、0から4までの要素を持っていたら、すべての警告が同じ変数を共有しているので "5"同じクロージングをすべて参照する別々のクロージャがあります。 – PMV

+0

はい、そうです。私の答えで1つの問題でオフ。 –

0

Soviutが言及しているように、悪い例では多くのイベントハンドラを作成しています。さらに、悪い例題の関数が同じi変数を参照していることを指摘することは重要です。つまり、実行時にすべての変数の最後の値がnodes.lengthになります。

これは、クロージャが作成されたためです。詳細はClosuresにあります。

+0

どちらの例も、イベントハンドラごとに別々の関数を作成します。良い例は、悪い例より効率的ではありません。しかし、各クロージャ内の 'i'変数の*現在の値を正しく取得します。 –

0

まず、悪い例では、イベントハンドラごとに関数が作成されます。ループは複数の関数オブジェクトを作成します。 2番目の例では、ループ内から単一の関数が作成され、参照されています。だから、たくさんのメモリを節約できます。

第2に、悪い例では、 "i"の値が実行されるので、関数は値を保持せず、実行時に常に "i"の最後の値を返します。しかし、良い例では、 "i"が関数に渡されるので、この値は関数のレキシカル環境として保持され、呼び出されると正しい値が返されます。

第3に、@Gary Hayesが述べたように、私たちは他の場所でもこの関数を使いたいかもしれません。だから、それをループから独立させておくのが最善です。

+1

良い答え。また、ループを実行することなく、他の場所でその関数を実行することもできます。 –

+0

はい非常に真実@GaryHayes ...私の答えを編集し、この優秀な点を追加して自由に感じてください! – poushy

+0

前の回答と同じ問題:良い例と悪い例の両方にノードがあるので、多くのクリック関数が作成されます。 – traktor53

1

ここではHTMLで確認できます:https://jsfiddle.net/vdrr4519/

'multifunc'要素は、多くの関数 'singlefunc'を使用して1つの例で挿入されています。クラスですべての要素を取り出し、それらを関数に渡します。

multifunc(document.querySelectorAll('.multifunc')); 

'for'ループを実行し、 'クリック'イベントリスナーを追加します。したがって、要素はクリック時にインデックスを警告する必要があります(0から始まります)。しかし、多くの関数を持つ例では、誤った値が生成されます(クロージャのために、他の回答も問題を強調します)。

私は、単一機能/複数の機能の問題ではないことも言わなければならないと思います。それはクロージャーを使用することの問題です。あなたは、私は多くの閉鎖を伴う実例を実装することができます:https://jsfiddle.net/pr7gqtdr/1/。私は基本的には単機能のハンドラで行うのと同じことを行うが、すべての時間は、新しい「ヘルパー」関数を呼び出します。最後に

nodes[i].onclick = function (i) { 
    return function (e) { 
     alert(i); 
    }; 
}(i); 

を参照してください、この(i)は即時関数呼び出しなので、onclickが取得しますクロージャ内で変数iが設定された関数。

しかし、単一の関数のオプションは、よりメモリ効率が良いので少し良くなります。関数はオブジェクトです。それらの多くを作成する場合は、一般的により多くのメモリを使用します。だから、これらから選んで、私は 'ハンドラ'機能のオプションに固執したいと思います。

関連する問題