JavaScript性能優化小知識總結
一直在學習javascript,也有看過《犀利開發Jquery內核詳解與實踐》,對這本書的評價只有兩個字犀利,可能是對javascript理解的還不夠透徹異或是自己太笨,更多的是自己不擅於思考懶得思考以至於裏面說的一些精髓都沒有太深入的理解。鑒於想讓自己有一個提升,進入一個更加廣闊的天地,總得找一個屬於自己的居所好好生存,所以平時會有意無意的去積累一些使用jQuerry的常用知識,特別是對於性能要求這一塊,總是會想是不是有更好的方式來實現。下面是我總結的一些小技巧,僅供參考。(我先會說一個總標題,然後用一小段話來說明這個意思 再最後用一個demo來簡單言明)
避免全局查找
在一個函數中會用到全局對象存儲為局部變量來減少全局查找
function search() {
//當我要使用當前頁面地址和主機域名
alert(window.location.href + window.location.host);
}
//最好的方式是如下這樣 先用一個簡單變量保存起來
function search() {
var location = window.location;
……
}
定時器
如果針對的是不斷運行的代碼,不應該使用setTimeout,而應該是用setInterval,因為setTimeout每一次都會初始化一個定時器,而setInterval只會在開始的時候初始化一個定時器。
var timeoutTimes = 0;
function timeout() {
timeoutTimes++;
if (timeoutTimes < 10) {
setTimeout(timeout, 10);
}
}
timeout();
//可以替換為:
var intervalTimes = 0;
function interval() {
intervalTimes++;
if (intervalTimes >= 10) {
clearInterval(interv);
}
}
var interv = setInterval(interval, 10 );
字符串連接
如果要連接多個字符串,應該少使用+=,如
s+=a;
s+=b;
s+=c;
//應該寫成
s+=a + b + c;
而如果是收集字符串,比如多次對同一個字符串進行+=操作的話,最好使用一個緩存,使用JavaScript數組來收集,最後使用join方法連接起來
var buf = [];
for (var i = 0; i < 100; i++) {
buf.push(i.toString());
}
var all = buf.join("");
避免with語句
和函數類似 ,with語句會創建自己的作用域,因此會增加其中執行的代碼的作用域鏈的長度,由於額外的作用域鏈的查找,在with語句中執行的代碼肯定會比外面執行的代碼要慢,在能不使用with語句的時候盡量不要使用with語句
with (a.b.c.d) {
property1 = 1;
property2 = 2;
}
//可以替換為:
var obj = a.b.c.d;
obj.property1 = 1;
obj.property2 = 2;
數字轉換成字符串
一般最好用"" + 1來將數字轉換成字符串,雖然看起來比較醜一點,但事實上這個效率是最高的,性能上來說:
("" +) > String() > .toString() > new String()
如果定義了toString()方法來進行類型轉換的話,推薦顯式調用toString(),因為內部的操作在嘗試所有可能性之後,會嘗試對象的toString()方法嘗試能否轉化為String,所以直接調用這個方法效率會更高
浮點數轉換成整型
很多人喜歡使用parseInt(),其實parseInt()是用於將字符串轉換成數字,而不是浮點數和整型之間的轉換,我們應該使用Math.floor()或者Math.round()
多個類型聲明,即使用單var形式
在JavaScript中所有變量都可以使用單個var語句來聲明,這樣就是組合在一起的語句,以減少整個腳本的執行時間,就如上面代碼一樣,上面代碼格式也挺規範,讓人一看就明了。
插入叠代器
如var name=values[i]; i++;前面兩條語句可以寫成var name=values[i++]。(原理在於i++是先賦值,再自加)
使用直接量
var aTest = new Array(); //替換為
var aTest = [];
var aTest = new Object; //替換為
var aTest = {};
var reg = new RegExp(); //替換為
var reg = /../;
//如果要創建具有一些特性的一般對象,也可以使用字面量,如下:
var oFruit = new O;
oFruit.color = "red";
oFruit.name = "apple";
//前面的代碼可用對象字面量來改寫成這樣:
var oFruit = { color: "red", name: "apple" };
使用DocumentFragment優化多次append
一旦需要更新DOM,請考慮使用文檔碎片來構建DOM結構,然後再將其添加到現存的文檔中。
for (var i = 0; i < 1000; i++) {
var el = document.createElement_x(‘p‘);
el.innerHTML = i;
document.body.appendChild(el);
}
//可以替換為:
var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
var el = document.createElement_x(‘p‘);
el.innerHTML = i;
frag.appendChild(el);
}
document.body.appendChild(frag);
使用一次innerHTML賦值代替構建dom元素
對於大的DOM更改,使用innerHTML要比使用標準的DOM方法創建同樣的DOM結構快得多。
var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
var el = document.createElement_x(‘p‘);
el.innerHTML = i;
frag.appendChild(el);
}
document.body.appendChild(frag);
//可以替換為:
var html = [];
for (var i = 0; i < 1000; i++) {
html.push(‘ ‘ + i + ‘ ‘);
}
document.body.innerHTML = html.join(‘‘);
通過模板元素clone,替代createElement_x
很多人喜歡在JavaScript中使用document.write來給頁面生成內容。事實上這樣的效率較低,如果需要直接插入HTML,可以找一個容器元素,比如指定一個div或者span,並設置他們的innerHTML來將自己的HTML代碼插入到頁面中。通常我們可能會使用字符串直接寫HTML來創建節點,其實這樣做,1、無法保證代碼的有效性;2、字符串操作效率低,所以應該是用document.createElement_x()方法,而如果文檔中存在現成的樣板節點,應該用cloneNode()方法,因為使用createElement_x()方法之後,你需要設置多次元素的屬性,使用cloneNode()則可以減少屬性的設置次數——同樣如果需要創建很多元素,應該先準備一個樣板節點。
var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
var el = document.createElement_x(‘p‘);
el.innerHTML = i;
frag.appendChild(el);
}
document.body.appendChild(frag);
//替換為:
var frag = document.createDocumentFragment();
var pEl = document.getElementsByTagName_r(‘p‘)[0];
for (var i = 0; i < 1000; i++) {
var el = pEl.cloneNode(false);
el.innerHTML = i;
frag.appendChild(el);
}
document.body.appendChild(frag);
使用firstChild和nextSibling代替childNodes遍歷dom元素
var nodes = element.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
}
//可以替換為:
var node = element.firstChild;
while (node) {
//……
node = node.nextSibling;
}
刪除DOM節點
刪除dom節點之前,一定要刪除註冊在該節點上的事件,不管是用observe方式還是用attachEvent方式註冊的事件,否則將會產生無法回收的內存。另外,在removeChild和innerHTML=""二者之間,盡量選擇後者。因為在sIEve(內存泄露監測工具)中監測的結果是用removeChild無法有效地釋放dom節點。
使用事件代理
任何可以冒泡的事件都不僅僅可以在事件目標上進行處理,目標的任何祖先節點上也能處理,使用這個知識就可以將事件處理程序附加到更高的地方負責多個目標的事件處理,同樣,對於內容動態增加並且子節點都需要相同的事件處理函數的情況,可以把事件註冊提到父節點上,這樣就不需要為每個子節點註冊事件監聽了。另外,現有的js庫都采用observe方式來創建事件監聽。其實現上隔離了dom對象和事件處理函數之間的循環引用,所以應該盡量采用這種方式來創建事件監聽。
重復使用的調用結果,事先保存到局部變量
避免多次取值的調用開銷
//避免多次取值的調用開銷
var h1 = element1.clientHeight + num1;
var h2 = element1.clientHeight + num2;
//可以替換為:
var eleHeight = element1.clientHeight;
var h1 = eleHeight + num1;
var h2 = eleHeight + num2;
註意NodeList
最小化訪問NodeList的次數可以極大的改進腳本的性能
var images = document.getElementsByTagName_r(‘img‘);
for (var i = 0, len = images.length; i < len; i++) {
}
編寫JavaScript的時候一定要知道何時返回NodeList對象,這樣可以最小化對它們的訪問
- 進行了對getElementsByTagName_r()的調用
- 獲取了元素的childNodes屬性
- 獲取了元素的attributes屬性
- 訪問了特殊的集合,如document.forms、document.images等等
要了解了當使用NodeList對象時,合理使用會極大的提升代碼執行速度。
優化循環
可以使用下面幾種方式來優化循環
1、減值叠代
大多數循環使用一個從0開始、增加到某個特定值的叠代器,在很多情況下,從最大值開始,在循環中不斷減值的叠代器更加高效
2、簡化終止條件
由於每次循環過程都會計算終止條件,所以必須保證它盡可能快,也就是說避免屬性查找或者其它的操作,最好是將循環控制量保存到局部變量中,也就是說對數組或列表對象的遍歷時,提前將length保存到局部變量中,避免在循環的每一步重復取值。
var list = document.getElementsByTagName_r(‘p‘);
for (var i = 0; i < list.length; i++) {
//……
}
//替換為:
var list = document.getElementsByTagName_r(‘p‘);
for (var i = 0, l = list.length; i < l; i++) {
//……
}
3、簡化循環體
循環體是執行最多的,所以要確保其被最大限度的優化
4、使用後測試循環
在JavaScript中,我們可以使用for(;;),while(),for(in)三種循環,事實上,這三種循環中for(in)的效率極差,因為他需要查詢散列鍵,只要可以,就應該盡量少用。for(;;)和while循環,while循環的效率要優於for(;;),可能是因為for(;;)結構的問題,需要經常跳轉回去。
var arr = [1, 2, 3, 4, 5, 6, 7];
var sum = 0;
for (var i = 0, l = arr.length; i < l; i++) {
sum += arr[i];
}
//可以考慮替換為:
var arr = [1, 2, 3, 4, 5, 6, 7];
var sum = 0, l = arr.length;
while (l--) {
sum += arr[l];
}
最常用的for循環和while循環都是前測試循環,而如do-while這種後測試循環,可以避免最初終止條件的計算,因此運行更快。
展開循環
當循環次數是確定的,消除循環並使用多次函數調用往往會更快。
避免雙重解釋:如果要提高代碼性能,盡可能避免出現需要按照JavaScript解釋的字符串,也就是
1、盡量少使用eval函數:使用eval相當於在運行時再次調用解釋引擎對內容進行運行,需要消耗大量時間,而且使用Eval帶來的安全性問題也是不容忽視的。
2、不要使用Function構造器
3、不要給setTimeout或者setInterval傳遞字符串參數
var num = 0;
setTimeout(‘num++‘, 10);
//可以替換為:
var num = 0;
function addNum() {
num++;
}
setTimeout(addNum, 10);
條件分支
將條件分支,按可能性順序從高到低排列:可以減少解釋器對條件的探測次數
在同一條件子的多(>2)條件分支時,使用switch優於if:switch分支選擇的效率高於if,在IE下尤為明顯。4分支的測試,IE下switch的執行時間約為if的一半。
使用三目運算符替代條件分支
if (a > b) {
num = a;
} else {
num = b;
}
//可以替換為:
num = a > b ? a : b;
JavaScript性能優化小知識總結