ホームページ > ウェブフロントエンド > jsチュートリアル > 効率性の問題とそれに関連する JavaScript_javascript スキルの for ループの最適化の詳細な調査

効率性の問題とそれに関連する JavaScript_javascript スキルの for ループの最適化の詳細な調査

WBOY
リリース: 2016-05-16 15:10:52
オリジナル
1602 人が閲覧しました

Underscore.js ライブラリ

1 日 (週) でループを何回書きましたか?

var i;
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 doSomeWorkOn(someThing);
}

ログイン後にコピー

もちろん無害ですが、醜くて奇妙で、特に文句を言うべきものではありません。しかし、この書き方はありきたりすぎます。

var i,
 j;
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 for(j = 0; j < someThing.stuff.length; j++) {
   doSomeWorkOn(someThing.stuff[j]);
 }
}

ログイン後にコピー

間違ったコードを拡張しているのに、大量の if をスローする前に、すでに気が狂っています。
もう2年もループを書いていない。
「何言ってるの?」
それは本当です、悪い冗談です。実際には、何も書いていないわけではありません (もちろん、いくつか書きました)。なぜなら、私はループを書かず、コードの方が理解しやすいからです。
どうやって?

_.each(someArray, function(someThing) {
 doSomeWorkOn(someThing);
})

ログイン後にコピー

さらに良いのは:

_.each(someArray, doSomeWorkOn);

ログイン後にコピー

これが underscorejs の動作です。すっきりしていて、シンプルで、読みやすく、短く、中間変数や大量のセミコロンがなく、シンプルで非常にエレガントです。
さらにいくつかの例を示します。

var i,
 result = [];
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 // 打到这,我已经手疼了
 if(someThing.isAwesome === true) {
   result.push(someArray[i]);
 }
}

ログイン後にコピー

繰り返しますが、ループを使用して時間を無駄にする典型的な使用例です。これらのサイトが反喫煙と菜食主義を推進しているにもかかわらず、これらの規定を見ると憤りを感じます。簡単な書き方を見てみましょう。

var result = _.filter(someArray, function(someThing) {
 return someThing.isAwesome === true;
})

ログイン後にコピー

アンダースコア内のフィルター名と同様、わずか 3 行のコードで新しい配列を取得できます。
それとも、これらの配列を別の形式に変換しますか?

var result = _.map(someArray, function(someThing) {
 return trasformTheThing(someThing);
})

ログイン後にコピー

上記の 3 つの例は日常生活では十分ですが、これらの関数はアンダースコアを表に置くには十分ではありません。

var grandTotal = 0,
 somePercentage = 1.07,
 severalNumbers = [33, 54, 42],
 i; // don't forget to hoist those indices;
for(i = 0; i < severalNumbers.length; i++) {
 var aNumber = severalNumbers[i];
 grandTotal += aNumber * somePercentage;
}

ログイン後にコピー

アンダースコアバージョン

var somePercentage = 1.07,
 severalNumbers = [33, 54, 42],
 grandTotal;
grandTotal = _.reduce(severalNumbers, function(runningTotal, aNumber) {
 return runningTotal + (aNumber * somePercentage);
}, 0)

ログイン後にコピー

これは最初は少し奇妙に思えるかもしれませんが、reduce に関するドキュメントを調べてその存在を知りました。私はループの使用を拒否しているので、これが私の第一選択です。上記はほんの紹介にすぎませんが、underscorejs ライブラリには素晴らしい機能もたくさんあります。

30 日間の未使用サイクル チャレンジ。

今後 30 日間はループを使用しないでください。不快で粗雑なものが大量にある場合は、それらをそれぞれまたはマップに置き換えてください。もう少し減らして使用してください。

Underscore は関数型プログラミングへの入り口であることに注意する必要があります。目に見える、目に見えない方法。良い方法です。


OurJS 注* 最新のブラウザは現在、filter、map、reduce の各メソッドをサポートしていますが、アンダースコア ライブラリは古いバージョンの IE との互換性を実現できます。以下は、ES5 ネイティブ メソッドを使用して記述された例です:

[3,4,5,3,3].forEach(function(obj){
  console.log(obj);
});

[1,2,3,4,5].filter(function(obj){
  return obj < 3
});

[9,8,5,2,3,4,5].map(function(obj){
  return obj + 2;
});

[1,2,3,4,5].reduce(function(pre, cur, idx, arr) {
  console.log(idx);  //4 个循环: 2-5
  return pre + cur;
});  //15

//sort方法同样很有用
[9,8,5,2,3,4,5].sort(function(obj1, obj2){
  return obj1 - obj2;
});

ログイン後にコピー

for in および for ループ

誰かが、for in の効率が for ループの効率よりもはるかに低いと指摘しました。次に、大規模な配列を処理するときに、さまざまなブラウザーで for in、for ループ、および forEach を使用する効率をテストしてみましょう。

現在、ほとんどのオープンソース ソフトウェアは for ループで配列の長さをキャッシュします。一般的な見解では、一部のブラウザーでは Array.length が配列の長さを毎回再計算するため、通常は配列の長さを格納するために一時変数が使用されます。事前に次のように指定します。

for (var idx = 0, len = testArray.length; idx < len; idx++) {
 //do sth.
}
ログイン後にコピー
ログイン後にコピー

キャッシュありとキャッシュなしのパフォーマンスの違いもテストします。

また、各テスト ループに合計演算を追加して、それが空のループではないことを示します。

for (var idx = 0, len = testArray.length; idx < len; idx++) {
 //do sth.
}
ログイン後にコピー
ログイン後にコピー

キャッシュありとキャッシュなしのパフォーマンスの違いもテストします。

また、各テスト ループに合計演算を追加して、それが空のループではないことを示します。

テスト コードは次のとおりです。[実行] をクリックして表示します
HTML コード

<h4 id="browser"></h4>
<table id="results" class="table"></table>
ログイン後にコピー

JavaScript コード

function () {

 //准备测试数据, 有200万条数据的大数组
 var testArray = []
  , testObject = {}
  , idx
  , len = 2000000
  , tmp = 0
  , $results = $("#results")
  , $browser = $("#browser")
  ;

 $browser.html(navigator.userAgent);
 $results.html('');

 for (var i = 0; i < len; i++) {
  var number = Math.random(); //若希望加快运算速度可使用取整:Math.random() * 10 | 0
  testArray.push(number);
  testObject[i] = number;
 }

 $results.append('<tr><th>测试代码</th><th>计算结果</th><th>所需时间,毫秒</th></tr>');

 //测试函数
 var test = function(testFunc) {
  var startTime
   , endTime
   , result
   ;

  startTime = new Date();
  tmp = 0;
  testFunc();
  endTime  = new Date();

  //计算测试用例(Test Case)运行所需要的时间
  result = endTime - startTime;
  $results.append('<tr><td><pre class="brush:php;toolbar:false">{0}
{1}{2}'.format(testFunc.toString(), tmp | 0, result)); }; test(function() { //测试for in 的效率 for (idx in testArray) { tmp += testArray[idx]; //经测试,idx是string类型,可能是慢的原因之一 } }); test(function() { //测试for loop循环的效率 for (idx = 0, len = testArray.length; idx < len; idx++) { tmp += testArray[idx]; } }); test(function() { //测试forEach的效率 testArray.forEach(function(data) { tmp += data; }); }); test(function() { //测试不缓存Array.length时效率 for (idx = 0; idx < testArray.length; idx++) { tmp += testArray[idx]; } }); test(function() { //测试使用{} (Object) 存健值对时,使用for in的效率如何 for (idx in testObject) { tmp += testObject[idx]; } }); test(function() { //测试从{} Object查值时的效率如何(这里的健key值事先己知) for (idx = 0, len = testArray.length; idx < len; idx++) { tmp += testObject[idx]; } }); }
ログイン後にコピー

実行[しばらくお待ちください]
テスト結果
テスト結果は計算によって異なる場合があります。これは、私のマシンで実行されている Firefox、Chrome、IE のテスト結果の概要です。

2016313110044207.jpg (971×678)

以下は観察されたいくつかの結論です

  • for in は for ループよりもはるかに遅く、Chrome では少なくとも 20 倍遅くなります
  • FF は forEach (ES5) を最適化しており、そのパフォーマンスは for ループよりも優れていますが、Chrome/IEn のパフォーマンスは劣っています
  • FF/Chrome の Array.length キャッシュは、直接使用するよりも少し遅くなります。 IE の最新バージョンを除いて、パフォーマンスの向上は最小限です (これは非常に予想外です)
  • 場合によっては、FFのJSエンジンの性能がV8より優れていると思われる
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート