JS程式碼的執行效率往往直接影響了頁面的效能,有的時候,實現同樣的功能,不同的JS程式碼往往在效率上相差很多,有的時候僅僅是由於我們的書寫習慣導致的,當然在高級點的瀏覽器中,它們大多已經幫我們優化了,但是在中國,萬惡的IE6仍然大量的存在,我們不得不去考慮它。對於JS程式碼的最佳化,實際上有很多的情況,有些影響是比較小的,而有些是比較嚴重的,本文中,我把幾個我認為影響比較嚴重的情況列出來,供大家參考。
1、字串的拼接
字串的拼接在我們開發中會經常遇到,所以我把其放在第一位,我們往往習慣的直接用=的方式來拼接字串,其實這種拼接的方式效率非常的低,我們可以用一種巧妙的方法來實現字串的拼接,那就是利用陣列的join方法。
<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) + "毫秒"); }
我們看看其在不同瀏覽器下執行的情況
#我們會發現,在IE6下其差別是相當明顯的,其實這種情況在IE的高版本中體現的也非常明顯,但是在Firefox下卻沒有多大的區別,相反第二種的相對效率還要低點,不過只是差別2ms左右,而Chrome也和Firefox類似。另外在這裡順便說明一下,在我們為數組添加元素的時候,很多人喜歡用數組的原生的方法push,其實直接用arr[i]或者arr[arr.length]的方式要快一點,大概在10000次循環的情況IE瀏覽器下會有十幾毫秒的差異。
2、for迴圈
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) + "毫秒"); }
由上表可以看出,在IE6.0下,其差別是非常明顯,而在Firefox和Chrome下幾乎沒有差別,之所以在IE6.0下會有這種情況,主要是因為for循環在執行中,第一種情況會每次都計算一下長度,而第二種情況卻是在開始的時候計算長度,並把其保存到一個變數中,所以其執行效率要高點,所以在我們使用for循環的時候,特別是需要計算長度的情況,我們應該開始將其保存到變數中。但並不是只要是取長度都會出現如此明顯的差別,如果我們只是操作一個數組,取得的是一個數組的長度,那麼其實兩種方式的寫法都差不多,我們看下面的例子:
<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) + "毫秒"); }
從上表可以看出,如果只是一個陣列的話,我們看到其實兩種寫法都是差不多的,其實如果我們把循環再上調到100000次的話,也只是差別幾毫秒而已,所以在陣列的情況下,我認為都是一樣的。對於for循環的優化,也有人提出很多點,有人認為用-=1,或者從大到小的方式循環等等,我認為是完全沒有必要的,這些優化往往實際情況下根本沒有表現出來,換句話說只是電腦層級的微小的變化,但是帶給我們的卻是程式碼的可讀性大大的降低,所以實在是得不償失。
3、減少頁面的重繪
減少頁面重繪雖然本質不是JS本身的優化,但是其往往是由JS引起的,而重繪的情況往往是嚴重影響頁面性能的,所以完全有必要拿出來,我們看下面例子:
<p id="demo"></p> <input type="button" value="效率低" onclick="func1()" /> <input type="button" value="效率高" onclick="func2()" /> var str = "<p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p><p>这是一个测试字符串</p>"; //效率低的 function func1(){ var obj = document.getElementById("demo"); var start = new Date().getTime(); for(var i = 0; i < 100; i++){ obj.innerHTML += str + i; } var end = new Date().getTime(); alert("用时 " + (end - start) + " 毫秒"); } //效率高的 function func2(){ var obj = document.getElementById("demo"); var start = new Date().getTime(); var arr = []; for(var i = 0; i < 100; i++){ arr[i] = str + i; } obj.innerHTML = arr.join(""); 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中文網其他相關文章!