2011-01-20 21 views
2

内部関数をこの外部関数の外に移動すると、関数が呼び出されるたびに作成されないようになりますか?ローカル機能にマイクロ最適化機能がないのですか?

この特定のケースでは、doMoreStuff機能はdoStuffの内部でのみ使用されます。私はこれらのようなローカル機能を持​​つことについて心配する必要がありますか?

function doStuff() { 
    var doMoreStuff = function(val) { 
     // do some stuff 
    } 

    // do something 
    for (var i = 0; i < list.length; i++) { 
     doMoreStuff(list[i]); 
     for (var j = 0; j < list[i].children.length; j++) { 
       doMoreStuff(list[i].children[j]); 
     } 
    } 
    // do some other stuff 

} 

actaulの例では、と言うことになります。

function sendDataToServer(data) { 
    var callback = function(incoming) { 
     // handle incoming 
    } 

    ajaxCall("url", data, callback); 

} 
+0

。他の考慮点もあります。この例では、内部にある関数によって作成されるクロージャーは非常に貴重です。パラメーターとして渡したり、何かを再処理することなく、他のすべてのローカル変数をカプセル化できます。 – davin

答えて

4

これはカテゴリ "ミクロの最適化" に該当するかどうかわかりません。私はいいえと言うだろう。

しかし、それはどのくらいの頻度でdoStuffと呼ばれますか。それを頻繁に呼び出すと、関数を何度も何度も作成するだけでは不必要で、間違いなくオーバーヘッドが追加されます。

あなたはグローバルスコープで「ヘルパー機能」を持っているが、それを再作成したくない場合は、あなたがそうのようにそれをラップすることができます。返される関数は閉鎖されたよう

var doStuff = (function() { 
    var doMoreStuff = function(val) { 
     // do some stuff 
    } 
    return function() { 
     // do something 
     for (var i = 0; i < list.length; i++) { 
      doMoreStuff(list[i]); 
     } 
     // do some other stuff 
    } 
}()); 

は、それがdoMoreStuffにアクセスできます。外側の関数はすぐに実行されます((function(){...}()))。

それとも、関数への参照を保持するオブジェクトを作成します。カプセル化、オブジェクトの作成パターンと他の概念について

var stuff = { 
    doMoreStuff: function() {...}, 
    doStuff: function() {...} 
}; 

詳しい情報は、本JavaScript Patternsで見つけることができます。

+0

それは非常に興味深い概念です。クロージャを使用して静的でプライベートなように動作させます。 – Raynos

+0

@Raynos:データをカプセル化したり、オブジェクトのプライベートなメンバーを定義したりするのによく使用されます.JavaScriptの使い慣れた方法に応じて、[JavaScriptのパターン](http://www.amazon。 com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752)。 –

+0

私は2番目のものに精通していますが、そのようなクロージャを使用して関数を "インライン化"することは、私には新しいものです。 – Raynos

0

それは完全に関数が呼び出される頻度によって異なります。それが毎秒10回呼び出されるOnUpdate関数であれば、それは適切な最適化です。ページあたり3回呼び出された場合、それは微細最適化です。

ネストされた便利な関数定義は必要ありませんが、関数の余分な引数で置き換えることはできます。ネストされた関数と

例:代わりに引数を指定して、今

function somefunc() { 
    var localvar = 5 

    var otherfunc = function() { 
     alert(localvar); 
    } 

    otherfunc(); 
} 

同じこと、:

function otherfunc(localvar) { 
    alert(localvar); 
} 

function somefunc() { 
    var localvar = 5 

    otherfunc(localvar); 
} 
+0

入れ子関数を避ける方法を説明できますか?私は主にコードをDRYに保ちます。その特定の機能の外に置くことは、本当にローカルになるべきグローバルな名前空間に多くを追加するだけです。理想的には私は静的な機能を望んでいますが、それは不可能です。 – Raynos

+0

確かに、ハングアップ:) – orlp

+0

ローカル関数を使用する理由は、主に 'somefunc'の中に' otherfunc'を含めることです。なぜなら、それがそこでしか使われておらず、インライン展開が少し醜いからです。 – Raynos

0

これは絶対にマイクロ最適化です。まず最初に関数を持つ理由は、コードをよりきれいにし、メンテナンスしやすく、読みやすくするためです。関数は、コードのセクションにセマンティック境界を追加します。各機能は1つのことだけを行うべきであり、それはきれいに行うべきです。したがって、同時に複数の機能を実行する関数を見つけたら、複数のルーチンにリファクタリングする候補があります。

あなたはそれが(それはまだ働いていない場合、それは、最適化するのは時期尚早だ。期間)遅すぎるの作業何かを持っている場合にのみ最適化します。覚えておいてください。

:プログラムがまだ終了していないことを考慮すると、時期尚早の最適化でもあります。なぜそれは悪いですか?さて、最初は長期的には重要でないかもしれないことに取り組んでいます。次に、最適化が現実的な意味で何かを改善したかどうかを確認するためのベースラインはありません。第3に、メンテナンス性と可読性を低下させて実行する前に実行するため、きれいで簡潔なコードを実行した場合よりも実行するのが難しくなります。第4に、あなたがそれを完了し、あなたのすべての必要性を理解するまで(おそらく、正確な詳細によるが、可能性の範囲外ではない、ロングショット)、プログラムのどこかにdoMoreStuffが必要かどうかは分かりません。迅速な「ベンチマーク」は、平均的なPC上で実行

+0

*これまで:15 char – orlp

+0

ありがとう@ nightcracker。 fixed ... – ircmaxell

0

Donnaldクヌースは時期尚早の最適化は諸悪のルートであると述べている理由があります

は...(私は、多くの変数不明-がためにそこにいることを知っていますそれほど明白にコメントをいけないが、それは)いかなる場合においても興味深いです:

count = 0; 
t1 = +new Date(); 
while(count < 1000000) { 
    p = function(){}; 
    ++count; 
} 
t2 = +new Date(); 
console.log(t2-t1); // milliseconds 

それはないが、それは、約100ミリ秒でダウンタイムを実行しているもたらします(たとえば、条件に増分を移動することで最適化できますの相違にが影響する3回を実行している機能の作成とないトゥイーン、それは本当に関係ありませんので)

を与えた:

462 
458 
464 

913 
878 
890 

を次に3つのランは与えた、機能の作成行をコメントアウト純粋に1000,000の空の関数の作成には、約0.5秒を追加します。オリジナルのコードがハンドヘルドデバイスで1秒間に10回実行されていると仮定しても(デバイスの全体的なパフォーマンスはこのノートパソコンの100分の1ですが、これは誇張されています - おそらく1/10に近いですが、これは、このコンピュータの1000回の機能作成/秒に相当し、1/2000秒で発生します。だから毎秒ハンドヘルドデバイスは処理の1/2000秒のオーバーヘッドを追加しています...毎秒半分のミリ秒はそれほど多くありません。

この基本テストから、私はPC上ではこれは間違いなくマイクロ最適化であると結論づけました。弱いデバイス用に開発しているのであれば、ほぼ確実です。

+0

そして、それを意味のある計算を行う関数本体またはループ本体と比較すると、その違いは日付の統計エラーの境界にあります。 – Raynos

+0

@レイノス、私はあなたがやったベンチマークを知っていませんが、より大きなループボディでテストすることは目的を破るでしょう。ループ本体を最小限に保つと、関数作成の効果をより正確に測定できるので、日付エラーの影響を最小限に抑えることができます。それに加えて何度もテストを実行し、正確な措置を取るという事実があります。私のポイントは、ループ本体が問題ではなく、あなたの質問は本質的に、私はコードを変更することでどれくらい節約できるのか、答えは関数コストを作成するのにどれだけ時間がかかるかということです。 – davin

+0

私が言っていることは、それが少し意味のある最適化だと思ったということでした。私は本当にあまり保存しません。私はいつも悪い習慣だと思った。 – Raynos

0

元の質問は2011年に尋ねられました。それ以来、Node.jsの台頭を考えると、問題を再検討する価値はあると思いました。サーバー環境では、ここ数ミリ秒で大したことがあります。それは負荷の下で応答する残りの間の違いかもしれません。

内部的な機能は概念的には優れていますが、JavaScriptエンジンのコードオプティマイザに問題が生じる可能性があります。次の例では、これを説明する:

function a1(n) { 
    return n + 2; 
} 

function a2(n) { 
    return 2 - n; 
} 

function a() { 
    var k = 5; 
    for (var i = 0; i < 100000000; i++) { 
     k = a1(k) + a2(k); 
    } 
    return k; 
} 

function b() { 
    function b1(n) { 
     return n + 2; 
    } 

    function b2(n) { 
     return 2 - n; 
    } 

    var k = 5; 
    for (var i = 0; i < 100000000; i++) { 
     k = b1(k) + b2(k); 
    } 
    return k; 
} 

function measure(label, fn) { 
    var s = new Date(); 
    var r = fn(); 
    var e = new Date(); 
    console.log(label, e - s); 
} 

for (var i = 0; i < 4; i++) { 
    measure('A', a); 
    measure('B', b); 
} 

コードを実行するためのコマンド:

node --trace_deopt test.js 

出力:あなたが見ることができるように

[deoptimize global object @ 0x2431b35106e9] 
A 128 
B 130 
A 132 
[deoptimizing (DEOPT eager): begin 0x3ee3d709a821 b (opt #5) @4, FP to SP delta: 72] 
    translating b => node=36, height=32 
    0x7fffb88a9960: [top + 64] <- 0x2431b3504121 ; rdi 0x2431b3504121 <undefined> 
    0x7fffb88a9958: [top + 56] <- 0x17210dea8376 ; caller's pc 
    0x7fffb88a9950: [top + 48] <- 0x7fffb88a9998 ; caller's fp 
    0x7fffb88a9948: [top + 40] <- 0x3ee3d709a709; context 
    0x7fffb88a9940: [top + 32] <- 0x3ee3d709a821; function 
    0x7fffb88a9938: [top + 24] <- 0x3ee3d70efa71 ; rcx 0x3ee3d70efa71 <JS Function b1 (SharedFunctionInfo 0x361602434ae1)> 
    0x7fffb88a9930: [top + 16] <- 0x3ee3d70efab9 ; rdx 0x3ee3d70efab9 <JS Function b2 (SharedFunctionInfo 0x361602434b71)> 
    0x7fffb88a9928: [top + 8] <- 5 ; rbx (smi) 
    0x7fffb88a9920: [top + 0] <- 0 ; rax (smi) 
[deoptimizing (eager): end 0x3ee3d709a821 b @4 => node=36, pc=0x17210dec9129, state=NO_REGISTERS, alignment=no padding, took 0.203 ms] 
[removing optimized code for: b] 
B 1000 
A 125 
B 1032 
A 132 
B 1033 

は、関数AとBが同じで走りました最初に速度。何らかの理由で最適化解除イベントが発生しました。それ以降、Bはほぼ一桁遅くなります。

パフォーマンスが重要なコードを書く場合は、内部機能を避けることが最善です。

関連する問題