javascript代碼優化

註釋javascript

        代碼中的註釋很重要,天然也是毋庸置疑的。一般咱們會強調代碼中註釋數量的多少,而輕視了對註釋質量的提升。編碼是及時添加註釋,會給後續代碼的維護人員帶來很大的便利。可是若是註釋不注意更新,或者因爲拷貝、粘貼引發的錯誤的註釋,則會誤導閱讀人員,反而給閱讀帶來障礙。
        除了註釋要及時更新外,咱們還應對註釋的內容要特別關注。註釋要儘可能簡單、清晰明瞭,避免使用含混晦澀的語言,同時着重 註釋的意義,對不太直觀的部分進行註解。JavaScript 的註釋有兩種"//" 和"/* .... */"。
        建議 // 用做代碼行註釋,html

  /* .... */ 形式用做對整個代碼段的註銷,或較正式的聲明中,如函數參數、功能、文件功能等的描述中。前端

命名規則java

JavaScript 中的標識符的命名規則:jquery

        一、以字母、下劃線'_'或美圓符號'$'開頭數組

        二、容許名稱中包含字母,數字,下劃線'_'和美圓符號'$'瀏覽器

        三、區分大小寫 變量、參數、成員變量、函數等名稱均以小寫字母開頭,構造器的名稱以大寫字母開頭。下劃線'_'開頭的變量通常習慣於標識私有 / 局部成員。而美圓符號'$'開頭的變量習慣於標識系統相關。閉包

建議:app

        $開頭變量表示jquery-dom節點。dom

        全大寫表常量(js無常量,用這種方式區分)。

        普通變量用駱峯命名(treeName)。

        全局變量或閉包變量用駱峯命名(TreeName)。

        函數儘可能用動名詞組合(如setName)。

        變量儘可能用名詞。

變量的聲明    

        儘管 JavaScript 語言並不要求在變量使用前先對變量進行聲明。但咱們仍是應該養成這個好習慣。這樣能夠比較容易的檢測出那些未經聲明的變量,避免其變爲隱藏的全局變量,形成隱患。 在函數的開始應先用 var 關鍵字聲明函數中要使用的局部變量,註釋變量的功能及表明的含義。每一個變量單獨佔一行,以便添加註釋。這是由於 JavaScript 中只有函數的 {} 代表做用域,用 var 關鍵字聲明的局部 變量只在函數內有效,而未經 var 聲明的變量則被視爲全局變量。

減小頁面重繪

        減小頁面重繪雖然本質不是JS自己的優化,可是其每每是由JS引發的,而重繪的狀況每每是嚴重影響頁面性能的,因此徹底有必要拿出來,咱們看下面例子:

 <div id="demo"></div>   
 <input type="button" value="效率低" onclick="func1()" />   
 <input type="button" value="效率高" onclick="func2()" /> 
 var str = "<div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div><div>這是一個測試字符串</div>";   
 //效率低的   
 function func1(){   
     var obj = document.getElementById("demo");   
     var start = new Date().getTime();   
     for(var i = 0; i < 100; i++){   
         obj.innerHTML += str + i;   
     }   
     var end = new Date().getTime();   
     alert("用時 " + (end - start) + " 毫秒");   
 }   
 //效率高的   
 function func2(){   
     var obj = document.getElementById("demo");   
     var start = new Date().getTime();   
     var arr = [];   
     for(var i = 0; i < 100; i++){   
         arr[i] = str + i;   
     }   
     obj.innerHTML = arr.join("");   
     var end = new Date().getTime();   
     alert("用時 " + (end - start) + " 毫秒");   
 } 

        在例子中,我只是用了100次的循環,由於若是用10000次循環的話,瀏覽器基本上就卡住不動了,可是即便是100次的循環,咱們看看下面的執行結果。

  

        能夠看到的是,這簡直是一個驚人的結果,僅僅100次的循環,出現瞭如此之大的差異。這裏還要注意的是,通常影響頁面重繪的不只僅是innerHTML,若是改變元素的樣式,位置等狀況都會觸發頁面重繪,因此在平時必定要注意這點。

for循環

        for循環是咱們常常會遇到的狀況,咱們先看看下面例子:

 <input type="button" value="效率低" onclick="func1()" />   
 <input type="button" value="效率高" onclick="func2()" /> 
 var arr = [];   
 for(var i = 0; i < 10000; i++){   
     arr[i] = "<div>" + i + "</div>";   
 }   
 document.body.innerHTML += arr.join("");   
     
 //效率低的   
 function func1(){   
     var divs = document.getElementsByTagName("div");   
     var start = new Date().getTime();   
     for(var i = 0; i < divs.length; i++){   
         //"效率低"   
     }   
     var end = new Date().getTime();   
     alert("用時:" + (end - start) + "毫秒");   
 }   
 //效率高的   
 function func2(){   
     var divs = document.getElementsByTagName("div");   
     var start = new Date().getTime();   
     for(var i = 0, len = divs.length; i < len; i++){   
         //"效率高"   
     }   
     var end = new Date().getTime();   
     alert("用時:" + (end - start) + "毫秒");   
 } 

        這裏for循環在執行中,第一種狀況會每次都計算一下長度,而第二種狀況倒是在開始的時候計算長度,並把其保存到一個變量中,因此其執行效率要高點,因此在咱們使用for循環的時候,特別是須要計算長度的狀況,咱們應該開始將其保存到一個變量中。

 <input type="button" value="效率低" onclick="func1()" />   
 <input type="button" value="效率高" onclick="func2()" />  
 var arr2 = [];   
 for(var i = 0; i < 10000; i++){   
     arr2[i] = "<div>" + i + "</div>";   
 }   
 //效率低的   
 function func1(){   
     var start = new Date().getTime();   
     for(var i = 0; i < arr2.length; i++){   
         //"效率低"   
     }   
     var end = new Date().getTime();   
     alert("用時:" + (end - start) + "毫秒");   
 }   
 //效率高的   
 function func2(){   
     var start = new Date().getTime();   
     for(var i = 0, len = arr2.length; i < len; i++){   
         //"效率高"   
     }   
     var end = new Date().getTime();   
     alert("用時:" + (end - start) + "毫秒");   
 } 

 

        對於for循環的優化,也有人提出不少點,有人認爲用-=1,或者從大到小的方式循環等等,我認爲是徹底沒有必要的,這些優化每每實際狀況下根本沒有表現出來,換句話說只是計算機級別的微小的變化,可是給咱們帶來的倒是代碼的可讀性大大的下降,因此實在是得不償失。

減小做用域鏈上的查找次數

        咱們知道,js代碼在執行的時候,若是須要訪問一個變量或者一個函數的時候,它須要遍歷當前執行環境的做用域鏈,而遍歷是從這個做用域鏈的前端一級一級的向後遍歷,直到全局執行環境,因此這裏每每會出現一個狀況,那就是若是咱們須要常常訪問全局環境的變量對象的時候,咱們每次都必須在當前做用域鏈上一級一級的遍歷,這顯然是比較低效一點的。

避免雙重解釋

        雙重解釋的狀況也是咱們常常會碰到的,有的時候咱們沒怎麼考慮到這種狀況會影響到效率,雙重解釋通常在咱們使用eval、new Function和setTimeout等狀況下會遇到,咱們看看下面的例子:

 <div id="demo"></div>   
 <input id="but1" type="button" onclick="func1()" value="效率低"/>   
 <input id="but2" type="button" onclick="func2()" value="效率高"/>   
 var sum, num1 = 1, num2 = 2;   
 function func1(){   
     var start = new Date().getTime();   
     for(var i = 0; i < 10000; i++){   
         var func = new Function("sum+=num1;num1+=num2;num2++;");   
         func();   
     }   
     var end = new Date().getTime();   
     alert("用時 " + (end - start) + " 毫秒");   
 }   
     
 function func2(){   
     var start = new Date().getTime();   
     for(var i = 0; i < 10000; i++){   
         sum+=num1;   
         num1+=num2;   
         num2++;   
     }   
     var end = new Date().getTime();   
     alert("用時 " + (end - start) + " 毫秒");   
 } 
 var sum, num1 = 1, num2 = 2;   
 function func1(){   
     var start = new Date().getTime();   
     for(var i = 0; i < 1000; i++){   
         eval("sum+=num1;num1+=num2;num2++;");   
     }   
     var end = new Date().getTime();   
     alert("用時 " + (end - start) + " 毫秒");   
 }   
 function func2(){   
     var start = new Date().getTime();   
     for(var i = 0; i < 1000; i++){   
         sum+=num1;   
         num1+=num2;   
         num2++;   
     }   
     var end = new Date().getTime();   
     alert("用時 " + (end - start) + " 毫秒");   
 } 

  雙重解釋是有必定開銷的,因此在實際當中要儘可能避免雙重解釋。

使用一次innerHTML賦值代替構建dom元素

對於大的DOM更改,使用innerHTML要比使用標準的DOM方法建立一樣的DOM結構快得多。

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('');

條件分支

        將條件分支,按可能性順序從高到低排列:能夠減小解釋器對條件的探測次數

        在同一條件子的多(>2)條件分支時,使用switch優於if:switch分支選擇的效率高於if,在IE下尤其明顯。4分支的測試,IE下switch的執行時間約爲if的一半。

        使用三元運算符替代條件分支

 if (a > b) {
            num = a;
        } else {
            num = b;
        }
        //能夠替換爲:
        num = a > b ? a : b;

循環引用

        若是循環引用中包含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。這時候造成了一個循環引用。

        下面2種方法能夠解決循環引用:        

        1)  置空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();

        2)  構造新的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的引用,從而打斷循環引用。

釋放dom元素佔用的內存

        將dom元素的innerHTML設置爲空字符串,能夠釋放其子元素佔用的內存。

        在rich應用中,用戶也許會在一個頁面上停留很長時間,可使用該方法釋放積累得愈來愈多的dom元素使用的內存。

釋放javascript對象

        在rich應用中,隨着實例化對象數量的增長,內存消耗會愈來愈大。因此應當及時釋放對對象的引用,讓GC可以回收這些內存控件。

        對象:obj = null

        對象屬性:delete obj.myproperty

        數組item:使用數組的splice方法釋放數組中不用的item

  函數節流(throttle)與函數去抖(debounce)

  函數去抖:

  若是用手指一直按住一個彈簧,它將不會彈起直到你鬆手爲止。

  也就是說當調用動做n毫秒後,纔會執行該動做,若在這n毫秒內又調用此動做則將從新計算執行時間。

var debounce = function(idle, action){
  var last
  return function(){
    var ctx = this, args = arguments
    clearTimeout(last)
    last = setTimeout(function(){
        action.apply(ctx, args)
    }, idle)
  }
}

  函數節流:

  若是將水龍頭擰緊直到水是以水滴的形式流出,那你會發現每隔一段時間,就會有一滴水流出。

  也就是會說預先設定一個執行週期,當調用動做的時刻大於等於執行週期則執行該動做,而後進入下一個新週期。

var throttle = function(delay, action){
  var last = Date.now();
  return function(){
    var curr = Date.now();
    if (curr - last > delay){
      action.apply(this, arguments)
      last = curr 
    }
  }
}
相關文章
相關標籤/搜索