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);
上記の各ループでは、ループ本体が実行されるたびにこの操作が発生します。
配列の順序を逆にすることで、ループのパフォーマンスを向上させることができます。
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 はパフォーマンスが悪く、直接警告が発行されます。逆反復のパフォーマンスはわずかに向上しますが、向上はそれほど大きくなく、コードの可読性は低下します。
概要:
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对应的结果只能是一个取值而不能是一系列的操作。
小结:
以上就是本文的全部内容,希望对大家的学习有所帮助。