2010年8月11日水曜日

javascript高速化のポイント12

WEBアプリケーションのパフォーマンスにおいて、RIAの普及によりクライアント側の実装も複雑化してきておりサーバ側だけでなくクライアント側のパフォーマンスも非常に重要な要素となっている今日この頃。


複雑なクライアントを作ったときに、javascriptのパフォーマンスが気になったので実装レベルのイディオム的に高速化できるポイントをまとめてみた(羅列してみた)。

まぁ他にもjavascriptのロード方法とかiframeの使い方とか、はたまたjQueryなどのフレームワークを使用しているとか色々あると思うが、それはまた次回ということで。

  • 1.length の参照を減らす
  • 2.タイマ値はなるべく長く
  • 3.同じイベントに登録する関数が多い場合は実行制御関数を使う
  • 4.withは使わない
  • 5.スコープを越えない、ローカルにコピーする
  • 6.関数はグローバルに定義する、スコープを深くしない
  • 7.ドット演算を減らす
  • 8.関数ポインタをキャッシュする
  • 9.文字列結合する場合は、+= じゃなくて join 使った方が高速
  • 10.style等でDOM要素のプロパティを見に行く場合、いちいちブラウザ側と通信が発生する
  • 11.配列に要素を追加するのはpushよりlengthの方が早い
  • 12.style操作時に画面非表示にする



1.length の参照を減らす


●forループ改善1
    
for (i = 0; i < elements.length; i++) {
    :
}    

     ↓
    
for (i = 0, len = elements.length; i < len; i++) {
    :
}


●forループ改善2
    
for(i in array){~}    

     ↓
    
for (i = 0, len = elements.length; i < len; i++) {
    :
}



            

2.タイマ値はなるべく長く


setIntervalの間隔が短いと重くなるのは当然だが、目安として50ms以上はとりたい。


3.同じイベントに登録する関数が多い場合は実行制御関数を使う


イベントに登録した関数の実行順序制御を行う場合、実行制御関数をイベントに登録して実際に実行される関数は配列などに登録していく方法があるが、実行順序の制御を行わない場合でも、この実装方法の方が複数回イベントに登録するよりオーバーヘッドが少ない。


実行制御関数実装方法(onload):http://buzzmemo.blogspot.com/2010/07/javascript.html



4.withは使わない


withを使うと新しいスコーブがスコープチェーンに追加される。スコープチェーンを作成するのにコストがかかるのと、
withの内側からwithの外側のスコープのプロパティを参照するためにはスコープチェーンをたどる必要があることからパフォーマンスが落ちる場合がある。


      

5.スコープを越えない、ローカルにコピーする


ローカル変数の参照はスコープチェーンをたどらなくて済む(検索の範囲が狭い)のでパフォーマンスが向上する。また、意図しないグローバルスコープの変数を定義したり参照したりすることを防ぐ。


      

6.関数はグローバルに定義する、スコープを深くしない


関数はなるべくグローバルに定義してスコープを深くしない方が速いらしいが、それほどの効果もなさそう。大規模になると名前空間を使わないと厳しいためそこまで気にする必要なし。

      

7.ドット演算を減らす。また、DOMのproperty, attributeへのアクセスを減らしてキャッシュする。


    
function BuildUI(){
    var baseElement = document.getElementById('target');
    baseElement.innerHTML =  '';            
    baseElement.innerHTML += BuildTitle();  
    baseElement.innerHTML += BuildBody();   
    baseElement.innerHTML += BuildFooter(); 
}

     ↓
function BuildUI(){
    var elementText = BuildTitle() + BuildBody() + BuildFooter();
    document.getElementById('target').innerHTML = elementText;
}

これだと1回の参照で済む。上記はDOMノードの参照において効果が大きく現れる。




8.関数ポインタをキャッシュする


Work関数はグローバルスコーブで定義されている関数とする。この関数呼び出しのたびにスコープチェーンをたどって関数を探しにいくことになる。
function IterateWorkOverCollection(){
    var length = myCollection.getItemCount();
    for(var index = 0; index
        Work(myCollection[index]);
    }
}

     ↓
グローバルスコープのWork関すへの参照(ポインタ)をローカル変数のfuncWorkに格納し、ループ内でそのローカル変数を使って関数を呼び出すことでパフォーマンスが向上する。
function IterateWorkOverCollection(){
    var funcWork = Work;
    var length = myCollection.getItemCount();
    for(var index = 0; index
        funcWork(myCollection[index]);
    }
}




9.文字列結合する場合は、+= じゃなくて join 使った方が高速


var run = {
TX_PLUS : function(){
    tx_clear(); var html = ''; for (var i = 0; i < count; i++){ html += 'あ'; } $('tx').innerHTML = html;
},
TX_JOIN : function(){
    tx_clear(); var html = []; for (var i = 0; i < count; i++) { html.push('あ'); } $('tx').innerHTML = html.join("");
}
};

参照サイト:http://www.drk7.jp/MT/archives/001313.html


      

10.style等でDOM要素のプロパティを見に行く場合、いちいちブラウザ側と通信が発生する


elm.style.background = "#FFF";
elm.style.width      = "500px";

     ↓
var elmstyle        = elm.style;
elmstyle.background = "#FFF";
elmstyle.width      = "500px";

参照サイト:http://www.songmu.jp/riji/archives/2008/10/jjug_cross_comm.html



11.配列に要素を追加するのはpushよりlengthの方が早い


ary.push(xxx);

     ↓
ary[ary.length]=xxx



12.style操作時に画面非表示にする


    if (obj.disabled) {
        obj.disabled = false;
    } else {
        obj.disabled = true;
    }

disabledを表示に変更するループ処理が異常に遅い。画面に反映(描写)する際の処理が遅いのか。(IEだけ??)対応としてbodyのdisplayをnoneに設定し画面を非表示にしてdisabled解除処理を行うことにより、描写しないため高速になった。
※unload処理だったので上記対応が可能だったが、画面の見え方に影響あり