JavaScriptの実行速度を向上させる方法(ループ編)


Nicholasによると、4つのコードがスクリプトの実行を遅らせ、最終的にスクリプトの暴走を招くという.それぞれ,回数が多すぎる同期ループ,膨大な関数体,不適切な再帰,および不合理なDOM呼び出しである.この本は第一の原因を改めて述べている.最後に,従来のループ構造を置き換え,スクリプトの暴走を完全に回避できる開発モデルを示した.
【原文タイトル】Speed up your JavaScript,Part 1【原文作者】Nicholas C.Zakas訳文出典:http://cuimingda.com/2009/01/speed-up-your-javascript-part-1.html前回の投稿では、各ブラウザがスクリプトの暴走をどのような状況でポップアップするかについて説明しました.Internet Explorerでは、ブラウザが複数の文を実行するとスクリプトの実行が停止し、他のブラウザでは、スクリプトの実行が一定時間を超えるとプロンプトが表示されます.私たちが検討する核心的な問題は、これらのブラウザが暴走したスクリプトを検出する場合ではなく、スクリプトをより速く実行し、これらの警告を回避する方法です.スクリプトの暴走には、基本的に次の4つの理由があります.1.ループで多くの操作が実行されています.2.肥大化した関数体3.過剰な再帰4.過剰なDOM呼び出しこの投稿では、ループ内の過剰な操作に重点を置きます.ループの操作は同期して行われるので、1つのループを実行するのにかかる時間はループの回数に完全に依存します.このため、ループ実行時間が長すぎてブラウザをロックする場合があります.1つは循環体に多くの操作が含まれていること、2つは循環の回数が多すぎることである.どちらの場合も、ブラウザをロックし、スクリプトの暴走のヒントを表示することができます.この問題を解決するコツは、次の2つの問題で各ループを評価することです.1.このループは同期して実行しなければなりませんか.2.ループの中のデータは、順番に実行しなければなりませんか?両方の質問の答えが否定的であれば、ループ内の操作を分解することができます.重要なのは、コードの具体的な環境に基づいて、上記の2つの問題の答えを決定することです.典型的なサイクルは次のようになります.
for(var i=0; i < items.length; i++){
  process(items[i]);
}

一見、このサイクルはそれほど問題なく、長い間実行されるかどうかは、サイクルの回数にかかっています.ループの直後に他のコードが実行されるときにループの結果に依存する必要がない場合、最初の問題に対する答えは「いいえ」です.また、ループは毎回1つの数値しか処理されず、前回のループの結果に依存しないため、2番目の問題に対する答えも否定的であることがわかります.これは、ループが何らかの方法で分解され、ブラウザをロックしてスクリプトの暴走のヒントを表示することはありません.『Professional JavaScript,Second Edition』という本では、実行回数が非常に大きい幻について、以下の方法をお勧めします.
function chunk(array, process, context){
  setTimeout(function(){
      var item = array.shift();
      process.call(context, item);

      if (array.length > 0){
          setTimeout(arguments.callee, 100);
      }
  }, 100);
}

chunk()関数の用途は,1つの配列を小さなブロック処理(これも名前の由来)に分割し,3つのパラメータを伝達することである.処理する配列オブジェクト、処理関数、およびprocess()関数で対応するthisオブジェクトを設定するオプションのコンテキスト変数.最初のtimerは、操作間の遅延を処理するために使用されます(ここでは100ミリ秒に設定し、実際の必要に応じて自分で変更できます).この関数を実行するたびに、配列内の最初のオブジェクトが取り出され、process()関数に渡されて操作されます.このときprocess()に未処理のオブジェクトがある場合、別のtimerが起動し、繰り返し待機するために使用されます.上記のループは、次の方法でこの関数を使用できます.
chunk(items, process);

ここで配列はキュー(queue)の形式を採用しており、ループの過程で毎回変更されることに注意してください.配列の元の状態を変更する場合は、concat()関数を使用して、渡す前に現在の配列のコピーを作成する2つの方法について説明します.
chunk(items.concat(), process);

もう1つの選択肢は、chunk()関数を直接修正し、関数の内部で直接修正することです.
function chunk(array, process, context){
  var items = array.concat();   //clone the array
  setTimeout(function(){
      var item = items.shift();
      process.call(context, item);

      if (items.length > 0){
          setTimeout(arguments.callee, 100);
      }
  }, 100);
}

配列の内容は、次のタイマが有効になる前に変化する可能性があるため、インデックスを1つだけ保存するよりも安全です.ここで述べたchunk()関数は,サイクル性能を最適化する出発点にすぎない.必要に応じて、より多くの機能を持つように改善することができます.たとえば、配列内のすべてのオブジェクトが処理された後、関数コールバックを追加できます.このように関数を変更するかどうかにかかわらず、JavaScriptのコード開発モデルにすぎず、配列の処理性能を最適化したり、スクリプトの暴走の警告を回避したりすることができます.