メインコンテンツへ移動

JS 学習ノート 12_最適化

無料2015-04-14#JS#js性能优化

本稿では保守性の最適化、パフォーマンスの最適化、リリースの最適化を含む

一.保守性の最適化

1.コメントの追加

コメントはコードの可読性と保守性を向上させることができます。もちろん、理想的な状況はコメントでいっぱいですが、これは現実的ではありません。そのため、重要な場所にのみコメントを追加する必要があります:

  • 関数とメソッド:特に戻り値。直接見てわからないためです

  • 大きなコードブロック(機能モジュール):モジュールの機能を説明

  • 複雑なアルゴリズム:重要なポイントを記述し、理解を容易にする

  • Hack:どの問題を修正するためか、現在の方案は完成されているか、改善できるか

2.変数型を「暗示」する

初期値を通じて暗示します。例えば:

var found = false;
var count = 1;
var name = '';
var student = null;

3.デカップリング(階層化)

  • 構造層:HTML

  • 行動層:JS

  • 表現層:CSS

できるだけ「越権」しないでください。もし本当に越権したい場合でも、ドキュメントまたはコメントで説明すべきです。できるだけ密結合を避けてください。例えば:

  • JS-HTML:

      elem.innerHTML = '<div class="block"><h2>title</h2><p>content</p></div>';
      //最好用下面的方式代替(显示页面中的隐藏元素)
      //elem.style.display = 'block';
    
  • JS-CSS:

      elem.style.color = '#e0e0e0';
      //最好用下面的方式代替(设置类,不要直接设置样式)
      //elem.className = 'my_class';
    
  • JS-JS:

      //逻辑耦合
      elem.onclick = function(e){
        if(txt.value !== null && txt.value !== '' &&...){
          //DOM 操作
        }
      }
      //最好用下面的方式代替(分离逻辑功能块)
      function isValid(){
        return txt.value !== null && txt.value !== '' &&...;
      }
      function display(){
        //DOM 操作
      }
      elem.onclick = function(e){
        if(isValid()){
          display();
        }
      }
    

    論理結合を避けるいくつかの原則:

    • event オブジェクトを渡さず、必要なデータのみを渡す

    • イベントのトリガーはアクションを実行する唯一の方法であるべきではない

    • イベントハンドラはイベント関連データ(イベントソース、座標値など)のみを処理し、その後アプリケーションロジックに引き渡す

4.コーディング原則

  • オブジェクトの所有権を尊重し、他人のオブジェクトを勝手に修改しないでください。具体的な要求:

    • インスタンス/プロトタイプにプロパティやメソッドを追加しない

    • 既存のメソッドを上書きしない

    代替方案:

    • 合成:必要な機能を実装した新しいオブジェクトを作成し、必要なオブジェクトを参照

    • 継承:カスタムタイプを作成し、修改が必要なタイプを継承し、追加機能を追加

  • 名前空間を使用してグローバル変数を避ける。例えば:

      var mySystem = {};
      mySystem.mod1 = {...};
    
  • 定数を使用して保守性を向上させる。例えば:

      var Consts = {};
      Consts.IMG_PATH = '../imgs/';
      
    

    注意:定数には一般的な CSS クラス名和其它の保守に影響する値を含む

二.パフォーマンスの最適化

1.長いスコープチェーン検索を避ける

複数回参照する必要があるグローバル変数をローカル変数として保存

2.with 文をできるだけ使用しない

with 文はスコープを延長し、長いスコープ検索のオーバーヘッドが存在します。ローカル変数として保存することで代替できます(with ほど便利ではありませんが、多少はましです)

3.不要なプロパティ検索を避ける

  1. 繰り返し使用する値をローカル変数として保存する。例えば:

     //不要用下面的代码(6 次属性查找)
     var qs = window.location.href.substring(window.location.href.indexOf('?'));
     //应该用下面的代码(4 次,而且更具可读性)
     var url = window.location.href;
     var qs = url.substring(url.indexOf('?'));
    
  2. ループの最適化

    • 減算反復の方が速い(i--)

    • 終了条件を簡素化。毎回ループ時に終了条件をチェックするため、条件を簡素化すると効率が向上

    • ループ本体を簡素化。ループ内の計算量をできるだけ減らす

    • 後テストループ(do...while)を使用。最初のループ前の判断を回避できる

  3. ループの展開

ループ回数が確定している場合、ループを使用しない方がよい。ループにはループ作成と終了条件処理の追加オーバーヘッドが存在するため。例えば:

    for(i = 0;i < 2;i++){
      process(arr[i]);
    }
    //下面的比上面的快
    process(arr[0]);
    process(arr[1]);
    process(arr[2]);
    

ループ回数が確定していない場合、Duff テクニック(Tom Duff が発明)を使用して一部のループを展開し、効率を向上できる。例えば:

    //credit: Jeff Greenberg for JS implementation of Duff's Device
    //假设 values.length > 0
    var iterations = Math.ceil(values.length / 8);
    var startAt = values.length % 8;
    var i = 0;
    do {
        switch(startAt){
            case 0: process(values[i++]);
            case 7: process(values[i++]);
            case 6: process(values[i++]);
            case 5: process(values[i++]);
            case 4: process(values[i++]);
            case 3: process(values[i++]);
            case 2: process(values[i++]);
            case 1: process(values[i++]);
        }
        startAt = 0;
    } while (--iterations > 0);
    //以上代码来自:http://www.cnblogs.com/kylindai/archive/2013/12/04/3458476.html

またはもう一つのより速い Duff 方法:

    //credit: Speed Up Your Site (New Riders, 2003)
    var iterations = Math.floor(values.length / 8);
    var leftover = values.length % 8;
    var i = 0;
    if (leftover > 0){
        do {
            process(values[i++]);
        } while (--leftover > 0);
    }
    do {
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
    } while (--iterations > 0);
    //以上代码来自:http://www.cnblogs.com/kylindai/archive/2013/12/04/3458476.html

4. 二重解釈を避ける

二重解釈とは:js で js を解析すること。具体的には eval() 関数または new Function(strCode) または setTimeout(strCode) を使用

二重解釈の欠点:解析するために別のパーサーを起動する必要があり、大きなオーバーヘッドが存在し、直接解析よりずっと遅い

  1. ネイティブメソッドの方が速い

ネイティブメソッドは C/C++ でコンパイルされたモジュールであるため、はるかに速い

  1. switch は if-else より速い

理由は言いにくいが、CSDN を参照できる

  1. ビット演算は速くない

他の言語では数学演算をビット演算に簡素化すると計算速度を向上できるが、js ではそういうことはない。js 内部には 1 種類の数値タイプ(double)しかないため、ビット演算には変換が必要:double - int - ビット演算 - double。パフォーマンスは想像できる通り

  1. 文の数を減らす

    • 1 つの var で複数の変数を宣言

    • 反復値を挿入。arr[i++] で 1 つの文で処理し、i++ を独立した文にしない

    • 配列/オブジェクトリテラルを使用。コード行数が明らかに減る

  2. DOM の最適化

    • 現場更新を減らす。DocumentFragment を使用して最適化できる

    • innerHTML で DOM ノードを作成する方が、一堆のノードを作成して組み立てるより速いが、JS-HTML 結合の問題が存在する。慎重に���慮

    • イベントデリゲートを使用

    • リアルタイム更新コレクションに注意(NodeList、NamedNodeMap、HTMLCollection)

      引用をローカル変数として保存する他に、そのプロパティにアクセスするにも再クエリが必要であることに注意。例えば:var imgs = document.images; imgs.length にアクセスするにももう一度クエリが必要

三.リリースの最適化

  1. 検証ツール(例:JSLint)を使用してコードをチェックし、構文エラー以外の潜在的な問題を発見

  2. コメントを削除(セキュリティ上の考慮)

  3. js ファイルを結合し、外部ファイルの数をできるだけ減らす

  4. コードを圧縮(例:YUI 圧縮器)。コード自体のサイズを減らし、同時にコードを難読化してセキュリティを向上できるが、コードの難読化自体にリスクが存在し、エラーを引き起こす可能性

  5. サーバー圧縮機能を有効化。例えば gzip モジュール

コメント

コメントはまだありません

コメントを書く