前端效能優化JavaScript篇

江湖小浪蝦發表於2019-02-16

關於前端效能優化的討論一直都很多,包羅的知識也很多,可以說效能優化只有更好,沒有最好。前面我寫了一篇關於css優化的總結文章,今天再從javascript方面聊一聊。

1.從資源載入方面來說,瀏覽器的載入順序是按原始碼從上到下載入解析的,遇到link,script等資源都會阻塞頁面渲染,所以我們會把script放在</body>前面,我們還可以結合構建工具(webpack,gulp…)壓縮js檔案,抽離公共js、去掉空格、註釋,儘可能地讓js檔案變小,防止指令碼阻塞頁面渲染。

2.在寫程式碼的時候我們還要注意以下問題。

(1)減少作用域鏈上的查詢次數。我們知道,js程式碼在執行的時候,如果需要訪問一個變數或者一個函式的時候,它需要從當前執行環境的作用域鏈一級一級地向上查詢,直到全域性作用域。如果我們需要經常訪問全域性環境的變數物件的時候,我們每次都必須在當前作用域鏈上一級一級的遍歷,這顯然是比較耗時的。

function getTitle() {
    var h1 = document.getElementByTagName("h1");
    for(var i = 0, len = h1.length; i < len; i++) {
        h1[i].innerHTML = document.title + " 測試 " + i;
    }
}

上面這樣寫就非常耗時,我們可以優化一下:

function getTitle() {
    var doc = document;
    var h1 = doc.getElementByTagName("h1");
    for(var i = 0, len = h1.length; i < len; i++) {
        h1[i].innerHTML = doc.title + " 測試 " + i;
    }
}

通過建立一個指向document的區域性變數,就可以通過限制一次全域性查詢來改進這個函式的效能。

(2)閉包導致的記憶體洩露。
閉包可以保證函式內的變數安全,可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中,不會被自動清除。使用場合:設計私有的方法和變數。使用不當就會造成記憶體佔用過高。我們需要手動銷燬記憶體中的變數。

(3)儘量少用全域性變數,儘量使用區域性變數。全域性變數如果不手動銷燬,會一直存在,造成全域性變數汙染,可能很產生一些意想不到的錯誤,而區域性變數執行完成後,就被會被回收;

(4)使用classname代替大量的內聯樣式修改。很多時候我們會在使用者操作的時候,頁面元素樣式會進行相應的變化,這時候我們就可以把要變化的樣式寫成一個class,操作class變化,就能實現大量樣式的變化。

(5)批量元素繫結事件,可以使用事件委託。事件委託就是利用事件冒泡,只指定一個事件處理程式,就可以管理某一型別的所有事件。比如我們有100個li,每個li都要繫結click點選事件,就可以用事件委託。舉一個例子:我們需要給所有的button繫結click事件

<div id="box">
        <input type="button" id="add" value="新增" />
        <input type="button" id="remove" value="刪除" />
        <input type="button" id="move" value="移動" />
        <input type="button" id="select" value="選擇" />
    </div>

我們有可能會這樣寫

window.onload = function(){
            var Add = document.getElementById("add");
            var Remove = document.getElementById("remove");
            var Move = document.getElementById("move");
            var Select = document.getElementById("select");
            
            Add.onclick = function(){
                alert(`新增`);
            };
            Remove.onclick = function(){
                alert(`刪除`);
            };
            Move.onclick = function(){
                alert(`移動`);
            };
            Select.onclick = function(){
                alert(`選擇`);
            }
        }

用事件委託就可以這樣寫:

window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLocaleLowerCase() == `input`){
                    switch(target.id){
                        case `add` :
                            alert(`新增`);
                            break;
                        case `remove` :
                            alert(`刪除`);
                            break;
                        case `move` :
                            alert(`移動`);
                            break;
                        case `select` :
                            alert(`選擇`);
                            break;
                    }
                }
            }
            
        }

而且使用事件委託,還有一個好處就是,當你新增一個新的button,一樣的會繫結上click事件,這就是委託事件的好處。上面這樣的寫法是原生js的寫法,jquery可以這樣寫:

$("#box").on("click","input",function(event){
             var targetId = $(this).attr(`id`);
              switch(targetId){
                        case `add` :
                            alert(`新增`);
                            break;
                        case `remove` :
                            alert(`刪除`);
                            break;
                        case `move` :
                            alert(`移動`);
                            break;
                        case `select` :
                            alert(`選擇`);
                            break;
                    }             
})

這樣寫就簡便得多。

(6)避免不必要的DOM操作,儘量使用 ID 選擇器:ID選擇器是最快的,避免一層層地去查詢節點。

(7)型別轉換:把數字轉換成字串使用number + “” 。
雖然看起來比較醜一點,但事實上這個效率是最高的,效能上來說:

("" + ) > String() > .toString() > new String()  

(8)對字串進行迴圈操作,譬如替換、查詢,應使用正規表示式。因為本身JavaScript的迴圈速度就比較慢,而正規表示式的操作是用C寫成的語言的API,效能很好。

(9)物件查詢使用[“”]查詢要比.items()更快。這和前面的減少物件查詢的思路是一樣的,呼叫.items()增加了一次查詢和函式的呼叫。

(10)浮點數轉換成整型使用Math.floor()或者Math.round()。parseInt()是用於將字串轉換成數字,Math是內部物件,所以Math.floor()其實並沒有多少查詢方法和呼叫的時間,速度是最快的。

關於js效能優化來說,涉及到很多方面,特別是現在又出現很多的前端框架,優化方法又各有不同。上面說的這些只是很淺顯的東西,在開發中多注意一下就可以了,儘量精簡自己的程式碼。效能優化還需要繼續深入研究。

相關文章