高パフォーマンスな JavaScript ループ文と条件文_JavaScript スキル

WBOY
リリース: 2016-05-16 15:19:06
オリジナル
1164 人が閲覧しました

1. ループステートメント
ご存知のとおり、一般的に使用されるループ ステートメントには、for、while、do-while、for-in、forEach などがあります。 for-in と forEach のパフォーマンスが若干低いことを除けば、最初の 3 つの選択は通常、パフォーマンスを考慮するよりもニーズに基づいて行われます。今日はそれぞれのパフォーマンスをテストし、最も極端な環境でもパフォーマンスを維持できることを示します。どのような最適化が可能か。

まず、for-in と forEach が他のものより遅い理由について話しましょう。 for-in は通常、オブジェクトの属性名を調べるために使用されます。forEach が関数ベースの反復であるのに対し、各反復操作はインスタンス自体の属性とプロトタイプ チェーン上の属性を同時に検索するため、効率は明らかに低くなります。 (特別な注意が必要です) ただし、IE のすべてのバージョンがサポートしているわけではありません。必要に応じて、配列項目ごとに外部メソッドを呼び出すことによって発生するオーバーヘッドが速度の低下の主な原因です。

次に、for、while、do-while が各反復で何を行うかを見てみましょう。

var length = items.length;
for(var i = 0; i < length; i++)
 process(items[i]);

var j = 0;
while(j < length) 
 process(items[j++]);

var k = 0;
do {
 process(items[k++]);
} while(k < length);

ログイン後にコピー

上記の各ループでは、ループ本体が実行されるたびにこの操作が発生します。

  • 制御条件での数値の大小比較 (i < length)
  • 条件の結果が true であるかどうかを制御する比較 (i < length === true)
  • 1 つの自動インクリメント操作 (i++)
  • 1 つの配列検索 (items[i])
  • 1 つの関数呼び出し処理(items[i])

配列の順序を逆にすることで、ループのパフォーマンスを向上させることができます。

for(var i = items.length; i--; )
 process(items[i]);

var j = items.length;
while(j--) 
 process(items[j]);

var k = items.length - 1;
do {
 process(items[k]);
} while(k--);

ログイン後にコピー

この例では、逆順ループが使用され、減算演算がループ条件に組み込まれています。各制御条件は単純に 0 と比較されます。制御条件は true 値と比較され、ゼロ以外の数値は自動的に true に変換されますが、ゼロ値は false と同等です。実際、制御条件は 2 つの比較 (反復回数は合計より小さいか? それは真か?) から 1 つの比較 (それは真か?) に減ります。これは、反復ごとに 2 つの比較から 1 つに減り、ループ速度がさらに向上します。

パフォーマンステスト:

それで、これは本当にそうなのでしょうか?リアルマネーはブラウザ認証を恐れません。テスト コードは非常にシンプルで、8 つの異なる状況に対応する 8 つの関数をカプセル化しています (タイマーなしでは Firefox でプロファイル情報を印刷できません。理由は不明です):

// init array
var a = [];
var length = 10;
for(var i = 0; i < length; i++)
 a[i] = 1;

function for_in() {
 var sum = 0;
 for(var i in a) 
  sum += a[i];
}

function for_each() {
 var sum = 0;
 a.forEach(function(value, index, array) {
  sum += value;
 });
}

function for_normal() {
 var sum = 0;
 for(var i = 0; i < length; i++)
  sum += a[i];
}

function for_reverse() {
 var sum = 0;
 for(var i = length; i--; )
  sum += a[i];
}

function while_normal() {
 var sum = 0;
 var i = 0;
 while(i < length) 
  sum += a[i++];
}

function while_reverse() {
 var sum = 0;
 var i = length;
 while(i--) 
  sum += a[i];
}

function do_while_normal() {
 var sum = 0;
 var i = 0;
 do {
  sum += a[i++];
 } while(i < length);
}

function do_while_reverse() {
 var sum = 0;
 var i = length - 1;
 do {
  sum += a[i];
 } while(i--);
}

setTimeout(function() {
 console.profile();
 for_in();
 for_each();
 for_normal();  
 for_reverse();
 while_normal();
 while_reverse();
 do_while_normal();
 do_while_reverse();
 console.profileEnd();
}, 1000);

ログイン後にコピー

配列の長さが 100 の場合、Firefox での結果は実際に期待どおりであることがわかりました。for-each と for-in は非効率であり、逆順は順順よりわずかに効率的です。 (Chrome上のプロフィールは時間が短すぎるため表示されません)

データ量が 100 万に達すると、Firefox と Chrome での結果は予想どおりになりますが、若干異なります。 ff での for-in は for-each よりもパフォーマンスが優れていますが、chrome での for-in はパフォーマンスが悪く、直接警告が発行されます。逆反復のパフォーマンスはわずかに向上しますが、向上はそれほど大きくなく、コードの可読性は低下します。

概要:

  • 逆反復によりコードのパフォーマンスは確かにわずかに向上しますが、極端なパフォーマンスの最適化が追求されない限り、
  • を使用する必要はありません。
  • 通常のループを使用して配列を走査できる場合は、for-in および for-each を使用しないでください

2. 条件文
一般的な条件文には if-else と switch-case が含まれます。では、if-else 文をいつ使用するのか、switch-case 文をいつ使用するのか?

まず、単純な if-else ステートメントのコードを見てみましょう:

if (value == 0){
  return result0;
} else if (value == 1){
  return result1;
} else if (value == 2){
  return result2;
} else if (value == 3){
  return result3;
} else if (value == 4){
  return result4;
} else if (value == 5){
  return result5;
} else if (value == 6){
  return result6;
} else if (value == 7){
  return result7;
} else if (value == 8){
  return result8;
} else if (value == 9){
  return result9;
} else {
  return result10;
}
ログイン後にコピー

最悪の場合 (value=10)、正しい結果を返すまでに 10 回の判断が必要になる可能性があります。では、このコードを最適化するにはどうすればよいでしょうか。明らかな最適化戦略は、最も可能性の高い値が 5 または 10 であるかを事前に判断し、これら 2 つの判断を事前に行うことです。しかし、通常はわかりません (最も可能性の高い選択)。この場合、パフォーマンスを最適化するためにバイナリ ツリー検索戦略を採用できます。

if (value < 6){
  if (value < 3){
    if (value == 0){
      return result0;
    } else if (value == 1){
      return result1;
    } else {
      return result2;
    }
  } else {
    if (value == 3){
      return result3;
    } else if (value == 4){
      return result4;
    } else {
      return result5;
    }
  }
} else {
  if (value < 8){
    if (value == 6){
      return result6;
    } else {
      return result7;
    }
  } else {
    if (value == 8){
      return result8;
    } else if (value == 9){
      return result9;
    } else {
      return result10;
    }
  }
}
ログイン後にコピー

这样优化后我们最多进行4次判断即可,大大提高了代码的性能。这样的优化思想有点类似二分查找,和二分查找相似的是,只有value值是连续的数字时才能进行这样的优化。但是代码这样写的话不利于维护,如果要增加一个条件,或者多个条件,就要重写很多代码,这时switch-case语句就有了用武之地。

将以上代码用switch-case语句重写:

switch(value){
  case 0:
    return result0;
  case 1:
    return result1;
  case 2:
    return result2;
  case 3:
    return result3;
  case 4:
    return result4;
  case 5:
    return result5;
  case 6:
    return result6;
  case 7:
    return result7;
  case 8:
    return result8;
  case 9:
    return result9;
  default:
    return result10;
}
ログイン後にコピー

swtich-case语句让代码显得可读性更强,而且swtich-case语句还有一个好处是如果多个value值返回同一个结果,就不用重写return那部分的代码。一般来说,当case数达到一定数量时,swtich-case语句的效率是比if-else高的,因为switch-case采用了branch table(分支表)索引来进行优化,当然各浏览器的优化程度也不一样。

除了if-else和swtich-case外,我们还可以采用查找表。

var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10];

//return the correct result
return results[value];
ログイン後にコピー

当数据量很大的时候,查找表的效率通常要比if-else语句和swtich-case语句高,查找表能用数字和字符串作为索引,而如果是字符串的情况下,最好用对象来代替数组。当然查找表的使用是有局限性的,每个case对应的结果只能是一个取值而不能是一系列的操作。

小结:

  • 当只有两个case或者case的value取值是一段连续的数字的时候,我们可以选择if-else语句
  • 当有3~10个case数并且case的value取值非线性的时候,我们可以选择switch-case语句
  • 当case数达到10个以上并且每次的结果只是一个取值而不是额外的JavaScript语句的时候,我们可以选择查找表

以上就是本文的全部内容,希望对大家的学习有所帮助。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!