JS コードの実行効率は、ページのパフォーマンスに直接影響することがよくありますが、同じ機能を実現するために、異なる JS コードでは効率に大きな違いが生じることがよくあります。私たちが使用するブラウザのほとんどは最適化されていますが、中国では邪悪な IE6 が依然として多数存在しており、それを考慮する必要があります。 JS コードの最適化に関しては、実際にはさまざまな状況があり、その中には比較的影響が小さいものもあれば、より重大な影響があるものもあります。この記事では、参考までに、重大な影響があると思われるいくつかの状況をリストします。
1. 文字列のスプライシング
私たちの開発では文字列のスプライシングが頻繁に発生するため、私たちは += を使用して文字列を直接スプライシングすることに慣れています。文字列を結合するには、配列の結合メソッドを使用するという賢い方法を使用してください。
<p class="one" id="one"></p> <input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" /> //效率低的 function func1(){ var start = new Date().getTime(); var template = ""; for(var i = 0; i < 10000; i++){ template += "<input type='button' value='a'>"; } var end = new Date().getTime(); document.getElementById("one").innerHTML = template; alert("用时:" + (end - start) + "毫秒"); } //效率高的 function func2(){ var start = new Date().getTime(); var array = []; for(var i = 0; i < 10000; i++){ array[i] = "<input type='button' value='a'>"; } var end = new Date().getTime(); document.getElementById("one").innerHTML = array.join(""); alert("用时:" + (end - start) + "毫秒"); }
さまざまなブラウザでの実行を見てみましょう
実際、この状況は IE の上位バージョンでも非常に明らかであることがわかります。逆に、2 番目のタイプの相対効率はさらに低くなりますが、その差はわずか 2 ミリ秒程度であり、Chrome は Firefox と同様です。さらに、配列に要素を追加する場合、多くの人はプッシュのネイティブ メソッドを使用することを好みますが、実際には、arr[i] または arr[arr.length] を直接使用する方が高速です。 10000。複数のループの場合、IE ブラウザでは 10 ミリ秒以上の差が生じます。
2. for ループ
for ループは、次の例を見てみましょう: 上の表からわかるように、IE6.0 では、その違いは大きく異なります。これは明らかであり、IE6.0 でこれが発生する理由は主に、前者の場合は長さが毎回計算されるため、後者の場合は長さが計算されるためです。ただし、最初に長さを計算して変数に保存するため、for ループを使用する場合、特に長さを計算する必要がある場合は、変数に保存し始める必要があります。しかし、配列を操作して配列の長さを取得するだけであれば、それほど明らかな違いはありません。次の例を見てみましょう:
<input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" /> var arr = []; for(var i = 0; i < 10000; i++){ arr[i] = "<p>" + i + "</p>"; } document.body.innerHTML += arr.join(""); //效率低的 function func1(){ var ps = document.getElementsByTagName("p"); var start = new Date().getTime(); for(var i = 0; i < ps.length; i++){ //"效率低" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); } //效率高的 function func2(){ var ps = document.getElementsByTagName("p"); var start = new Date().getTime(); for(var i = 0, len = ps.length; i < len; i++){ //"效率高" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); }
上記の表からわかるように、配列だけであれば、2 つの書き方はほぼ同じであることがわかります。実際、ループを 10 万回まで増やしても、その違いはわずかです。ミリ秒なので、配列の場合も同様だと思います。 for ループの最適化に関しては、-=1 を使用するとか、大から小へループするなど、多くの指摘がなされていますが、これらの最適化はまったく不要であることが多いと思います。実際の状況を一言で言えば、コンピュータレベルでは小さな変更にすぎませんが、コードの可読性が大幅に低下するため、実際にはメリットがありません。
3. ページの再描画を減らす
ページの再描画を減らすことは本質的には JS 自体の最適化ではありませんが、多くの場合、JS が原因で発生し、再描画はページのパフォーマンスに重大な影響を与えることが多いため、次のことを確認する必要があります。次の例:<input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" /> var arr2 = []; for(var i = 0; i < 10000; i++){ arr2[i] = "<p>" + i + "</p>"; } //效率低的 function func1(){ var start = new Date().getTime(); for(var i = 0; i < arr2.length; i++){ //"效率低" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); } //效率高的 function func2(){ var start = new Date().getTime(); for(var i = 0, len = arr2.length; i < len; i++){ //"效率高" } var end = new Date().getTime(); alert("用时:" + (end - start) + "毫秒"); }
どのブラウザであっても、わずか 100 サイクル後には驚くべき結果が得られることがわかります。さらに、次のこともわかりました。実際、IE6 の実行効率は Firefox よりもはるかに優れています。Firefox がページの再描画に最適化を行っていないことがわかります。ここで、一般にページの再描画に影響するのは innerHTML だけではないことにも注意してください。要素のスタイルや位置などを変更すると、ページの再描画がトリガーされるため、通常はこれに注意する必要があります。
4. スコープ チェーンの検索数を減らす js コードが実行されるとき、変数または関数にアクセスする必要がある場合は、現在の実行環境のスコープ チェーンをトラバースする必要があることがわかっています。ここから始まります。スコープ チェーンのフロントエンドは、グローバル実行環境に至るまでレベルごとに逆方向にトラバースされます。そのため、ここでよくある状況が発生します。つまり、グローバル環境の変数オブジェクトに頻繁にアクセスする必要がある場合、常に現在のスコープ チェーン内のグローバル環境の変数オブジェクトにアクセスする場合、ドメイン チェーンをレベルごとに移動するのは明らかに時間がかかります。次の例を見てみましょう。
<p id="demo"></p> <input id="but1" type="button" onclick="func1()" value="效率低"/> <input id="but2" type="button" onclick="func2()" value="效率高"/> function func1(){ var start = new Date().getTime(); for(var i = 0; i < 10000; i++){ var but1 = document.getElementById("but1"); var but2 = document.getElementById("but2"); var inputs = document.getElementsByTagName("input"); var ps = document.getElementsByTagName("p"); var but1 = document.getElementById("but1"); var but2 = document.getElementById("but2"); var inputs = document.getElementsByTagName("input"); var ps = document.getElementsByTagName("p"); var but1 = document.getElementById("but1"); var but2 = document.getElementById("but2"); var inputs = document.getElementsByTagName("input"); var ps = document.getElementsByTagName("p"); var but1 = document.getElementById("but1"); var but2 = document.getElementById("but2"); var inputs = document.getElementsByTagName("input"); var ps = document.getElementsByTagName("p"); var but1 = document.getElementById("but1"); var but2 = document.getElementById("but2"); var inputs = document.getElementsByTagName("input"); var ps = document.getElementsByTagName("p"); var but1 = document.getElementById("but1"); var but2 = document.getElementById("but2"); var inputs = document.getElementsByTagName("input"); var ps = document.getElementsByTagName("p"); } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } function func2(){ var start = new Date().getTime(); var doc = document; for(var i = 0; i < 10000; i++){ var but1 = doc.getElementById("but1"); var but2 = doc.getElementById("but2"); var inputs = doc.getElementsByTagName("input"); var ps = doc.getElementsByTagName("p"); var but1 = doc.getElementById("but1"); var but2 = doc.getElementById("but2"); var inputs = doc.getElementsByTagName("input"); var ps = doc.getElementsByTagName("p"); var but1 = doc.getElementById("but1"); var but2 = doc.getElementById("but2"); var inputs = doc.getElementsByTagName("input"); var ps = doc.getElementsByTagName("p"); var but1 = doc.getElementById("but1"); var but2 = doc.getElementById("but2"); var inputs = doc.getElementsByTagName("input"); var ps = doc.getElementsByTagName("p"); var but1 = doc.getElementById("but1"); var but2 = doc.getElementById("but2"); var inputs = doc.getElementsByTagName("input"); var ps = doc.getElementsByTagName("p"); var but1 = doc.getElementById("but1"); var but2 = doc.getElementById("but2"); var inputs = doc.getElementsByTagName("input"); var ps = doc.getElementsByTagName("p"); } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒");
上面代码中,第二种情况是先把全局对象的变量放到函数里面先保存下来,然后直接访问这个变量,而第一种情况是每次都遍历作用域链,直到全局环境,我们看到第二种情况实际上只遍历了一次,而第一种情况却是每次都遍历了,所以我们看看其执行结果:
从上表中可以看出,其在IE6下差别还是非常明显的,而且这种差别在多级作用域链和多个全局变量的情况下还会表现的非常明显。
5、避免双重解释
双重解释的情况也是我们经常会碰到的,有的时候我们没怎么考虑到这种情况会影响到效率,双重解释一般在我们使用eval、new Function和setTimeout等情况下会遇到,我们看看下面的例子:
<p id="demo"></p> <input id="but1" type="button" onclick="func1()" value="效率低"/> <input id="but2" type="button" onclick="func2()" value="效率高"/> var sum, num1 = 1, num2 = 2; function func1(){ var start = new Date().getTime(); for(var i = 0; i < 10000; i++){ var func = new Function("sum+=num1;num1+=num2;num2++;"); func(); } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } function func2(){ var start = new Date().getTime(); for(var i = 0; i < 10000; i++){ sum+=num1; num1+=num2; num2++; } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); }
第一种情况我们是使用了new Function来进行双重解释,而第二种是避免了双重解释,我们看看在不同浏览器下的表现:
可以看到,在所有的浏览器中,双重解释都是有很大开销的,所以在实际当中要尽量避免双重解释。
感谢”SeaSunK”对第四点测试报告错误的指正,现在已经修改过来了。至于最后一点提出的func1每次都初始化,没有可比性,所以我给换了eval,结果发现,在IE6.0下还是有影响,而且在Firefox下,使用eval对效率的影响程度更加厉害,在Firefox下,如果10000次循环,需要十多秒的时间,所以我把循环都变成了1000次。看代码和报告。
var sum, num1 = 1, num2 = 2; function func1(){ var start = new Date().getTime(); for(var i = 0; i < 1000; i++){ eval("sum+=num1;num1+=num2;num2++;"); } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } function func2(){ var start = new Date().getTime(); for(var i = 0; i < 1000; i++){ sum+=num1; num1+=num2; num2++; } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒");
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上がJS コードを最適化する方法について (グラフィック チュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。