高效率JavaScript代碼的編寫技巧

來源:互聯網
上載者:User

使用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');

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.