2012-03-19 16 views
21

私はhttp://www.youtube.com/watch?v=mHtdZgou0qUを見ています。約13:37(hehe)に、スコープチェーンに新しいオブジェクトが追加されたために避けるべき事項のリストが表示されます。Javascript - クロージャーを控えめに使用する?

私は彼がusingtry-catchというステートメントと、範囲外の変数にアクセスすると言っていることを理解していますが、なぜクロージャを避けるべきか理解できません。クロージャのローカル変数がスコープチェーンの最上位にある場合、パフォーマンスの損失はどこにありますか?

+2

覚えておいてください:時期尚早最適化はすべての悪の根源です。このビデオでは、JavaScriptのパフォーマンス向上について議論が行われていますが、ほとんどのJavaScriptを最適化する必要はありません。ユーザーがボタンをクリックした後に非同期に機能を呼び出すと、30msの応答速度と40msの応答速度の差に気づかれることはありません。 – zzzzBov

+1

ここの答えはすべていいです。しかし、ビデオは数年前のことであり、かつてのように関連性がないかもしれないことを覚えておいてください。 –

答えて

20

これは、ローカルではない変数を検索するために、VMはスコープチェーンを見つけてそれらを見つける必要があるからです。一方、ローカル変数はキャッシュされているので、ローカル変数の参照ははるかに高速です。機能がネストされるほど、スコープチェーンが長くなり、潜在的なパフォーマンスの影響が増します。あなたが参照している場合、かなり顕著になる、はるかに高速になりそうそれらを見て、ここで

(function(window, document, undefined) { 
    // ... 
})(window, document); 

windowdocumentなるローカル変数:あなたは、多くの場合、人気のJSライブラリでこのようなコードを参照してください理由

この

は、一部でありますこれらのオブジェクトはコード内から数千回あります。

This pageには、スコープチェーンと実行コンテキストの説明があります。 (記事全体を読んでみると面白いです。)

しかし、現代のブラウザは全体的に、このようなものすべてを基本的に無視できるところまで最適化しているので、心配するものではありません。

+0

これは正しくあります。なぜなら、Closuresはローカル変数をコピーではなくローカル変数に格納するからです。そのため、ローカルルックアップではなくスタックウォークを強制します。 –

+0

私は、範囲外の変数にアクセスしないクロージャを意味します。スコープ外の変数へのアクセスを避け、明示的に閉鎖を避けると言うのは少し冗長に思えました。ローカル変数を使用することに加えて、クロージャも回避する必要があることを意味しているようです。彼が言っていることを誤解していますか? – mowwwalker

+2

@Walkerneo:非ローカル変数を参照していない場合は、クロージャではありません。可能な限り閉鎖を避けるべきですが、範囲外の変数を参照する必要がある場合は、ローカル変数を使用してルックアップを高速化する必要があります(変数をその関数内から頻繁に使用する場合は、それ以外のことはあまりしません)。 –

9

The linked video explains why closure can inflict some performance hits starting about 11:08.

基本的に、彼はネストされた各機能のために、それはスコープチェーンに別のオブジェクトを追加しますので、クロージャの外で変数にアクセスするにも時間がかかるだろうと言っています。

変数に関連付けられた値を見つけるためには、JavaScriptのinterprerは、次のプロセスに従います

  1. 1が機能しなかった場合場合は親スコープオブジェクト
  2. を検索し、ローカルスコープオブジェクト
  3. を検索します2が機能しなかった場合、親の親スコープオブジェクトを検索する
  4. 親スコープを検索し続けるまで
  5. グローバルスコープを検索する
  6. が見つからない場合は、未定義の変数エラーをスローします。

通常の関数では、変数を見つけるには、スコープチェーンの先頭を検索するだけです。一方、親変数を見つけるためのクロージャは、時にはいくつかのレベルの深さでスコープチェーンを検索する必要があります。 、それが中に3つのレベルアップトラバースしなければならない最も内側の関数から

function a (x) { 
    function b (y) { 
    return (function (z) { 
     return x + y + z; 
    })(y + y); 
    } 
    return b(x + 3); 
} 

、表現x + y + zを評価する:あなたはこのようないくつかのクロージャを持っている場合たとえば、(これは非常に不自然な例であることに注意してください)スコープチェーンをxにすると、スコープチェーンを再び2つのレベルでyを見つけ出し、最後にzを1回見つける必要があります。合計で、の範囲内の6つのオブジェクトを検索して最終結果を返さなければなりませんでした。

のクロージャは、常に親変数にアクセスする必要があるため、クロージャでは避けられないものです。彼らがなければクロージャを使用する目的はありません。

また、Javascriptでは、関数、特にクロージャの作成にかなりのオーバーヘッドがあります。例えば、この非常に単純な閉鎖ください:

function a(x) { 
    return function (y) { 
    return x + y; 
    } 
} 

をそして、あなたはいくつかの異なる回それを呼び出す、この

var x = a(1); 
var y = a(2); 
var z = a(3); 
alert(x(3)); // 4 
alert(y(3)); // 5 
alert(z(3)); // 6 

のようにあなたはaから返される関数は渡されたものを維持するために持っていることに気づくでしょう親関数が既に呼び出された後でも、親関数の引数として使用できます。これは、通訳者があなたが今までに呼び出されたすべての関数に渡したものを記憶しなければならないことを意味します。

+0

第3の関数のローカル変数として 'y'と' z'を格納しても、パフォーマンスは低下しますか?もちろん、この例ではクロージャを作成する理由はありませんが、存在するときはどうなりますか? – mowwwalker

+0

@Walkerneoそれでも、親変数を見つけるためにはスコープチェーンを上る必要があります。内部関数で親変数を複数回使用すると、ローカル変数として宣言する価値がありますが、一度しか使用しない場合は、ローカル変数として宣言するのに役立つものはありません。 –

+0

申し訳ありませんが、私はスコープチェーンを通過する際の問題を理解していますが、他の答えに関する私のコメントで言いましたように、範囲外の変数については、クロージャーに本来的に間違った何かがあり、スコープチェーンに別のオブジェクトを追加するだけではありません。 – mowwwalker

関連する問題