js 性能優化 篇一


JS性能優化

摘自:http://www.china125.com/design/js/3631.htm

 首先,由於JS是一種解釋型語言,執行速度要比編譯型語言慢得多。(注:,Chrome是第一款內置優化引擎,將JS編譯成本地代碼的瀏覽器,其它瀏覽器也陸續實現了JS的編譯過程。但是,即使到了編譯執行JS的新階段,仍然會存在低效率的代碼。)以下總結一些可以改進代碼的整體性能的方法。
 
1.注意作用域
 
記住一點,隨着作用域中的作用域數量的增加,訪問當前作用域以外的變量的時間也在增加。所以,訪問全局變量總是比訪問局部變量要慢,因為需要遍歷作用域鏈。只要能減少花費在作用域鏈上的時間,就能增加腳本的整體性能。
 
1). 避免全局查找(因為涉及作用域上的查找)
 
function updateUI() {
    var imgs = document.getElementByTagName("img");
    for(var i = 0, len = imgs.length; i < len; i++) {
        imgs[i].title = document.title + " image " + i;
    }
}
注意,updateUI中包含了二個對於全局變量document對象的引用,特別是循環中的document引用,查到次數是O(n),每次都要進行作用域鏈查找。通過創建一個指向document的局部變量,就可以通過限制一次全局查找來改進這個函數的性能。
 
function updateUI() {
    var doc = document;
    var imgs = doc.getElementByTagName("img");
    for(var i = 0, len = imgs.length; i < len; i++) {
        imgs[i].title = doc.title + " image " + i;
    }
}
2). 避免with語句(with會創建自已的作用域,因此會增加其中執行代碼的作用域的長度)
 
2.選擇正確的方法
 
和其它語言一樣,性能問題的一部分是和用於解決問題的算法或方法有關的,所以通過選擇正確的方法也能起到優化作用。
 
1.避免不必要的屬性查找
 
在JS中訪問變量或數組都是O(1)操作,比訪問對象上的屬性更有效率,后者是一個O(n)操作。對象上的任何屬性查找都要比訪問變量或數組花費更長時間,因為必須在原型鏈中對擁有該名稱的屬性進行一次搜索,即屬性查找越多,執行時間越長。所以針對需要多次用到對象屬性,應將其存儲在局部變量。
 
2.優化循環
 
循環是編程中最常見的結構,優化循環是性能優化過程中很重要的一部分。一個循環的基本優化步驟如下:
 
減值迭代——大多數循環使用一個從0開始,增加到某個特定值的迭代器。在很多情況下,從最大值開始,在循環中不斷減值的迭代器更加有效。
簡化終止條件——由於每次循環過程都會計算終止條件,故必須保證它盡可能快,即避免屬性查找或其它O(n)的操作。
簡化循環體——循環體是執行最多的,故要確保其被最大限度地優化。確保沒有某些可以被很容易移出循環的密集計算。
使用后測試循環——最常用的for和while循環都是前測試循環,而如do-while循環可以避免最初終止條件的計算,因些計算更快。
for(var i = 0; i < values.length; i++) {
    process(values[i]);
}
優化1:簡化終止條件
 
for(var i = 0, len = values.length; i < len; i++) {
    process(values[i]);
}
優化2:使用后測試循環(注意:使用后測試循環需要確保要處理的值至少有一個)
 
var i values.length - 1;
if(i > -1) {
    do {
        process(values[i]);
    }while(--i >= 0);
}
3.展開循環
 
當循環的次數確定時,消除循環並使用多次函數調用往往更快
當循環的次數不確定時,可以使用Duff裝置來優化。Duff裝置的基本概念是通過計算迭代的次數是否為8的倍數將一個循環展開為一系列語句。如下:
// Jeff Greenberg for JS implementation of Duff's Device
// 假設:values.length > 0
function process(v) {
    alert(v);
}
 
var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
var iterations = Math.ceil(values.length / 8);
var startAt = values.length % 8;
var i = 0;
 
do {
    switch(startAt) {
        case 0 : process(values[i++]);
        case 7 : process(values[i++]);
        case 6 : process(values[i++]);
        case 5 : process(values[i++]);
        case 4 : process(values[i++]);
        case 3 : process(values[i++]);
        case 2 : process(values[i++]);
        case 1 : process(values[i++]);
    }
    startAt = 0;
}while(--iterations > 0);
如上展開循環可以提升大數據集的處理速度。接下來給出更快的Duff裝置技術,將do-while循環分成2個單獨的循環。(注:這種方法幾乎比原始的Duff裝置實現快上40%。)
 
// Speed Up Your Site(New Riders, 2003)
function process(v) {
    alert(v);
}
 
var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
var iterations = Math.floor(values.length / 8);
var leftover = values.length % 8;
var i = 0;
 
if(leftover > 0) {
    do {
        process(values[i++]);
    }while(--leftover > 0);
}
 
do {
    process(values[i++]);
    process(values[i++]);
    process(values[i++]);
    process(values[i++]);
    process(values[i++]);
    process(values[i++]);
    process(values[i++]);
    process(values[i++]);
}while(--iterations > 0);
針對大數據集使用展開循環可以節省很多時間,但對於小數據集,額外的開銷則可能得不償失。
 
4.避免雙重解釋
 
當JS代碼想解析JS代碼時就會存在雙重解釋懲罰,當使用eval()函數或是Function構造函數以及使用setTimeout()傳一個字符串時都會發生這種情況。如下
 
eval("alert('hello world');"); // 避免
var sayHi = new Function("alert('hello world');"); // 避免
setTimeout("alert('hello world');", 100);// 避免
以上代碼是包含在字符串中的,即在JS代碼運行的同時必須新啟運一個解析器來解析新的代碼。實例化一個新的解析器有不容忽視的開銷,故這種代碼要比直接解析要慢。以下這幾個例子,除了極少情況下eval是必須的,應盡量避免使用上述。對於Function構造函數,直接寫成一般的函數即可。對於setTimeout可以傳入函數作為第一個參數。如下:
 
alert('hello world');
var sayHi = function() {
    alert('hello world');
};
setTimeout(function() {
    alert('hello world');
}, 100);
總之,若要提高代碼性能,盡可能避免出現需要按照JS解釋的代碼。
 
5.性能的其它注意事項
 
原生方法更快——只要有可能,使用原生方法而不是自已用JS重寫。原生方法是用諸如C/C++之類的編譯型語言寫出來的,要比JS的快多了。
switch語句較快——若有一系列復雜的if-else語句,可以轉換成單個switch語句則可以得到更快的代碼,還可以通過將case語句按照最可能的到最不可能的順序進行組織,來進一步優化。
位運算較快——當進行數學運算時,位運算操作要比任何布爾運算或算數運算快。選擇性地用位運算替換算數運算可以極大提升復雜計算的性能,諸如取模,邏輯與和邏輯或也可以考慮用位運算來替換。
3.最小化語句數
 
JS代碼中的語句數量也會影響所執行的操作的速度,完成多個操作的單個語句要比完成單個操作的多個語句塊快。故要找出可以組合在一起的語句,以減來整體的執行時間。這里列舉幾種模式
 
1.多個變量聲明
 
// 避免
var i = 1;
var j = "hello";
var arr = [1,2,3];
var now = new Date();
 
// 提倡
var i = 1,
    j = "hello",
    arr = [1,2,3],
    now = new Date();
2.插入迭代值
 
// 避免
var name = values[i];
i++;
 
// 提倡
var name = values[i++];
3.使用數組和對象字面量,避免使用構造函數Array(),Object()
 
// 避免 
var a = new Array();
a[0] = 1;
a[1] = "hello";
a[2] = 45;
 
var o = new Obejct();
o.name = "bill";
o.age = 13;
 
// 提倡
var a = [1, "hello", 45];
var o = {
    name : "bill",
    age : 13
};
4.優化DOM交互
 
在JS中,DOM無疑是最慢的一部分,DOM操作和交互要消耗大量時間,因為它們往往需要重新渲染整個頁面或者某一個部分,故理解如何優化與DOM的交互可以極大提高腳本完成的速度。
 
1.最小化現場更新
 
一旦你需要訪問的DOM部分是已經顯示的頁面的一部分,那么你就是在進行一個現場更新。之所以叫現場更新,是因為需要立即(現場)對頁面對用戶的顯示進行更新,每一個更改,不管是插入單個字符還是移除整個片段,都有一個性能懲罰,因為瀏覽器需要重新計算無數尺寸以進行更新。現場更新進行的越多,代碼完成執行所花的時間也越長。
 
2.多使用innerHTML
 
有兩種在頁面上創建DOM節點的方法:使用諸如createElement()和appendChild()之類的DOM方法,以及使用innerHTML。對於小的DOM更改,兩者效率差不多,但對於大的DOM更改,innerHTML要比標准的DOM方法創建同樣的DOM結構快得多。
 
當使用innerHTML設置為某個值時,后台會創建一個HTML解釋器,然后使用內部的DOM調用來創建DOM結構,而非基於JS的DOM調用。由於內部方法是編譯好的而非解釋執行,故執行的更快。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM