使用DocumentFragment最佳化多次append
添加多個dom元素時,先將元素append到DocumentFragment中,最後統一將DocumentFragment添加到頁面。該做法可以減少頁面渲染dom元素的次數。經IE和FX下測試,在append1000個元素時,效率能提高10%-30%,FX下提升較為明顯。
前:
for (var i = 0; i < 1000; i++) { var el = document.createElement('p'); el.innerHTML = i; document.body.appendChild(el);}
後:
var frag = document.createDocumentFragment();for (var i = 0; i < 1000; i++) { var el = document.createElement('p'); el.innerHTML = i; frag.appendChild(el);}document.body.appendChild(frag); 通過模板元素clone,替代createElement
通過一個模板dom對象cloneNode,效率比直接建立element高。效能提高不明顯,約為10%左右。在低於100個元素create和append操作時,沒有優勢。
var frag = document.createDocumentFragment();for (var i = 0; i < 1000; i++) { var el = document.createElement('p'); el.innerHTML = i; frag.appendChild(el);}document.body.appendChild(frag);
var frag = document.createDocumentFragment();var pEl = document.getElementsByTagName('p')[0];for (var i = 0; i < 1000; i++) { var el = pEl.cloneNode(false); el.innerHTML = i; frag.appendChild(el);}document.body.appendChild(frag); 使用一次innerHTML賦值代替構建dom元素
根據資料構建列表樣式的時候,使用設定列表容器innerHTML的方式,比構建dom元素並append到頁面中的方式,效率有數量級上的提高。
var frag = document.createDocumentFragment();for (var i = 0; i < 1000; i++) { var el = document.createElement('p'); el.innerHTML = i; frag.appendChild(el);}document.body.appendChild(frag);
var html = [];for (var i = 0; i < 1000; i++) { html.push('<p>' + i + '</p>');}document.body.innerHTML = html.join(''); 使用firstChild和nextSibling代替childNodes遍曆dom元素
約能獲得30%-50%的效能提高。逆向遍曆時使用lastChild和previousSibling。
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;} 使用Array做為StringBuffer,代替字串拼接的操作
IE在對字串拼接的時候,會建立臨時的String對象;經測試,在IE下,當拼接的字串越來越大時,運行效率會急劇下降。Fx和Opera都對字串拼接操作進行了最佳化;經測試,在Fx下,使用Array的join方式執行時間約為直接字串拼接的1.4倍。
var now = new Date();var str = '';for (var i = 0; i < 10000; i++) { str += '123456789123456789';}alert(new Date() - now);
var now = new Date();var strBuffer = [];for (var i = 0; i < 10000; i++) { strBuffer.push('123456789123456789');}var str = strBuffer.join('');alert(new Date() - now); 將迴圈控制量儲存到局部變數
對數組和列表對象的遍曆時,提前將length儲存到局部變數中,避免在迴圈的每一步重複取值。
var list = document.getElementsByTagName('p');for (var i = 0; i < list.length; i++) { ……}
var list = document.getElementsByTagName('p');for (var i = 0, l = list.length; i < l; i++) { ……} 順序無關的遍曆時,用while替代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];} 使用三目運算子覆寫準則分支
if (a > b) {num = a;} else {num = b;}
num = a > b ? a : b;
需要不斷執行的時候,優先考慮使用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); 使用function而不是string
如果把字串作為setTimeout和setInterval的參數,瀏覽器會先用這個字串構建一個function。
var num = 0;setTimeout('num++', 10);
var num = 0;function addNum () { num++;}setTimeout(addNum, 10); 重複使用的調用結果,事先儲存到局部變數
避免多次取值的調用開銷。
var h1 = element1.clientHeight + num1;var h2 = element1.clientHeight + num2;
var eleHeight = element1.clientHeight;var h1 = eleHeight + num1;var h2 = eleHeight + num2;
使用直接量
var a = new Array(param,param,...) -> var a = []var foo = new Object() -> var foo = {}var reg = new RegExp() -> var reg = /.../ 避免使用with
with雖然可以縮短代碼量,但是會在運行時構造一個新的scope。OperaDev上還有這樣的解釋,使用with語句會使得解譯器無法在文法解析階段對代碼進行最佳化。對此說法,無法驗證。
with (a.b.c.d) {property1 = 1;property2 = 2;}
var obj = a.b.c.d;obj.property1 = 1;obj.property2 = 2;
巧用||和&&布林運算子
function eventHandler (e) {if(!e) e = window.event;}
function eventHandler (e) {e = e || window.event;}
還有下面的例子:
if (myobj) {doSomething(myobj);}
myobj && doSomething(myobj);
類型轉換
數字轉換成字串,應用"" + 1,效能上:("" +) > String() > .toString() > new String();
浮點數轉換成整型,不使用parseInt(), parseInt()是用於將字串轉換成數字,而不是浮點數和整型之間的轉換,建議使用Math.floor()或者Math.round()。
對於自訂的對象,推薦顯式調用toString()。內部操作在嘗試所有可能性之後,會嘗試對象的toString()方法嘗試能否轉化為String。
循環參考
如果循環參考中包含DOM對象或者ActiveX對象,那麼就會發生記憶體泄露。記憶體泄露的後果是在瀏覽器關閉前,即使是重新整理頁面,這部分記憶體不會被瀏覽器釋放。
簡單的循環參考:
var el = document.getElementById('MyElement');var func = function () {…}el.func = func;func.element = el;
但是通常不會出現這種情況。通常循環參考發生在為dom元素添加閉包作為expendo的時候。
function init() { var el = document.getElementById('MyElement');el.onclick = function () {……}}init();
init在執行的時候,當前上下文我們叫做context。這個時候,context引用了el,el引用了function,function引用了context。這時候形成了一個循環參考。
置空dom對象
function init() {var el = document.getElementById('MyElement');el.onclick = function () {……}}init();
function init() {var el = document.getElementById('MyElement');el.onclick = function () {……}el = null;}init();
將el置空,context中不包含對dom對象的引用,從而打斷迴圈應用。如果我們需要將dom對象返回,可以用如下方法:
function init() { var el = document.getElementById('MyElement'); el.onclick = function () {……} return el;}init();
function init() {var el = document.getElementById('MyElement');el.onclick = function () {……}try{return el;} finally { el = null;}}init();
構造新的context:
function init() { var el = document.getElementById('MyElement'); el.onclick = function () {……}}init();
function elClickHandler() {……}function init() { var el = document.getElementById('MyElement'); el.onclick = elClickHandler;}init();
把function抽到新的context中,這樣,function的context就不包含對el的引用,從而打斷循環參考。
通過javascript建立的dom對象,必須append到頁面中
IE下,指令碼建立的dom對象,如果沒有append到頁面中,重新整理頁面,這部分記憶體是不會回收的!
function create () { var gc = document.getElementById('GC'); for (var i = 0; i < 5000 ; i++) { var el = document.createElement('div'); el.innerHTML = "test"; //下面這句可以注釋掉,看看瀏覽器在工作管理員中,點擊按鈕然後重新整理後的記憶體變化 gc.appendChild(el); } } 釋放dom元素佔用的記憶體
將dom元素的innerHTML設定為空白字串,可以釋放其子項目佔用的記憶體。
在rich應用中,使用者也許會在一個頁面上停留很長時間,可以使用該方法釋放積累得越來越多的dom元素使用的記憶體。
釋放javascript對象
在rich應用中,隨著執行個體化對象數量的增加,記憶體消耗會越來越大。所以應當及時釋放對對象的引用,讓GC能夠回收這些記憶體控制項。
對象:obj = null,對象屬性:delete obj.myproperty。
數組item:使用數組的splice方法釋放數組中不用的item。
避免string的隱式裝箱
對string的方法調用,比如'xxx'.length,瀏覽器會進行一個隱式的裝箱操作,將字串先轉換成一個String對象。推薦對聲明有可能使用String執行個體方法的字串時,採用如下寫法:
var myString = new String('Hello World');