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調用。因爲內部方法是編譯好的而非解釋執行,故執行的更快。
相關文章
相關標籤/搜索