前端工程最佳化:javascript的最佳化小結

來源:互聯網
上載者:User

前端工程最佳化:javascript的最佳化小結
我覺得最佳化javascript是一門高深的學問,在這裡也只能站在前人的肩膀上,說一些我淺顯的認識,更希望的是拋鑽引玉,如有不對,敬請斧正。   首先,要認識到是,最佳化js的關鍵之處在於,最佳化它的運行速度,以此為切入點。   javascript的最佳化原則是:二八原則      滿足考量大多數情況,而遇到極端情況,有能力則兼顧之,學會放棄,適當取捨;      原因是,影響使用者的體驗很重要的因素之一回應時間   0.1s: 使用者覺得很流暢  1.0s: 使用者的操作可能偶爾受到影響,並且使用者已經能感覺到有些不流暢  10s : 對使用者的影響比較嚴重,需要相應的進度提示。使用者也會有一些沮喪   (//我覺得10s太寬泛了,通常而言2s以上就受不了了)    當然js最佳化只是提升回應時間需要改善的方面的眾多之一,前端最佳化知識何其之深,只有深入瞭解,才會驚訝於前端所需要掌握的知識竟是如此之多,當然無形之中也會給人壓力||動力。扯遠了,但是還是想力薦兩篇關於前端最佳化的blog:前端工程開啟速度最佳化的循序漸進總結和web前端最佳化最佳實務及工具。   繼續正題,ok,那麼知道了目標是提升回應時間,加快運行速度,那麼具體有哪些可行的方案呢:   管理範圍  操作資料  流量控制  Reflow  DOM操作  長時間啟動並執行指令碼處理        管理範圍   舉個板栗:   <span style="font-size: 16px;">var foo = 1;function test(){    //對變數foo進行一系列操作} function test2(){    var foo = 1;     //對變數foo進行一系列操作}</span>    也就是說,局部變數存在於使用中的物件中,解析器只需尋找範圍中的單個對象。   在JavaScript中,我們應該儘可能的用局部變數來代替全域變數,這句話所有人都知道,可是這句話是誰先說的?為什麼要這麼做?有什麼根據嗎?不這麼做,對效能到底能帶來多大的損失?以下我摘自《JavaScript Variable Performance》的一段:      在如何提高JavaScript效能這個問題上,大家最常聽到的建議應該就是盡量使用局部變數(local variables)來代替全域變數(global variables)。在我從事Web開發工作的九年時間裡,這條建議始終縈繞在我的耳邊,並且從來沒有質疑過,而這條建議的基礎,則來自於 JavaScript處理範圍(scoping)和標識符解析(identifier resolution)的方法。   首先我們要明確,函數在JavaScript中具體表現為對象,建立一個函數的過程,其實也就是建立一個對象的過程。每個函數對象都有一個叫做 [[Scope]]的內部屬性,這個內部屬性包含建立函數時的範圍資訊。實際上,[[Scope]]屬性對應的是一個對象(Variable Objects)列表,列表中的對象是可以從函數內部訪問的。比如說我們建立一個全域函數A,那麼A的[[Scope]]內部屬性中只包含一個全域對象(Global Object),而如果我們在A中建立一個新的函數B,那麼B的[[Scope]]屬性中就包含兩個對象,函數A的Activation Object對象在前面,全域對象(Global Object)排在後面。 當一個函數被執行的時候,會自動建立一個可以執行的對象(Execution Object),並同時綁定一個範圍鏈(Scope Chain)。範圍鏈會通過下面兩個步驟來建立,用於進行標識符解析。 首先將函數對象[[Scope]]內部屬性中的對象,按順序複製到範圍鏈中。其次,在函數執行時,會建立一個新的Activation Object對象,這個對象中包含了this、參數(arguments)、局部變數(包括命名的參數)的定義,這個Activation Object對象會被置於範圍鏈的最前面。  在執行JavaScript代碼的過程中,當遇到一個標識符,就會根據標識符的名稱,在執行內容(Execution Context)的範圍鏈中進行搜尋。從範圍鏈的第一個對象(該函數的Activation Object對象)開始,如果沒有找到,就搜尋範圍鏈中的下一個對象,如此往複,直到找到了標識符的定義。如果在搜尋完範圍中的最後一個對象,也就是全域對象(Global Object)以後也沒有找到,則會拋出一個錯誤,提示使用者該變數未定義(undefined)。這是在ECMA-262標準中描述的函數執行模型和標識符解析(Identifier Resolution)的過程,事實證明,大部分的JavaScript引擎確實也是這樣實現的。需要注意的是,ECMA-262並沒有強制要求採用這種結構,只是對這部分功能加以描述而已。   瞭解標識符解析(Identifier Resolution)的過程以後,我們就能明白為什麼局部變數的解析速度要比其他範圍的變數快,主要是由於搜尋過程被大幅縮短了。   也就是:當標識符解析的過程需要進行深度搜尋時,會伴隨效能損失,而且效能損失的程度會隨著標識符深度的增加而遞增。       資料操作 使用局部變數,它是最快的  obj.name比obj.xxx.name訪問更快,訪問屬性的速度,與其在對象中的深度有關。“ . ”操作的次數直接影響著訪問對象屬性的耗時。   2.  緩衝頻繁使用的對象、數組及相關的屬性值       <span style="font-size: 16px;">function process(data){    var count = data.count;    if (count > 0){        for(var i = 0; i < count ; i++){            processData(data.item[i]);        }           }}</span>  3.  不直接操作NodeList,將其轉換成靜態數組後再使用    方法: Array.prototype.slice.call() => 標準瀏覽器       逐個拷貝到一個新數組中 => For IE     需要注意的是,遍曆NodeList時,不做對當前NodeList相關結構有影響的DOM操作,並且如之前所提到的,要緩衝一些頻繁使用到的屬性值,以免發生不必要的悲劇。板栗:     <span style="font-size: 16px;"> var divs = document.getElementsByTagName('DIV');         //假定頁面中有div,所以divs.length是大於0的    for (var idx = 0; idx < divs.length; idx++){        document.body.appendChild(            //杯具悄然而置            document.createElement('DIV')        );        console.info(divs.length);    }</span>  上面的代碼最後運行會報錯,原因通過不斷地往document.body下插入div 節點,for迴圈的終止條件( div.length也隨之改變)失效,陷入死迴圈。也就是說通過getElementsByTagName()擷取得到的是一個Live NodeList的引用,任何對其相關的DOM操作都會立即反應在這個NodeList上面。        Dom操作   1.  增刪查改  盡量使用DocumentFragment 處理節點時可以使用cloneNode()複製一份 若要對DOM進行直接修改,請先將其display:none;  2.  指明操作DOM的context   context.getElementsByTagName()   3.  拆分方法,一個方法解決一件事   拆分功能,讓一個方法只做一件事,通過不斷地調用方法來實現複雜功能,但是,這些簡單方法要避免相互交叉調用。     Be Lazy(使指令碼儘可能少地運行,或者不運行。)    短路運算式應用:如 a && b || c   基於事件去寫相應的處理方法 惰性函數       流量控制     <span style="font-size: 16px;">if(...){}elseif(...){}elseif(...){}elseif(...){}elseif(...){}elseif(...){}else{ }</span>  原則: 在if語句中,將經常會發生的條件,放在靠上的位置if的條件為連續的區間時,可以使用二分法的方式來拆分較多離散值的判斷,可以使用switch來替代使用數組查詢的方式要注意隱式的類型轉換小心遞迴   <span style="font-size: 16px;">var foo = 0;         if(foo == false){ //隱式轉換        ...     } </span>    <span style="font-size: 16px;">function recurse(){    recurse();}recurse(); //又是一個悲劇,會報錯,無限遞迴了</span>       Reflow   何為reflow,即是:在CSS規範中有一個渲染對象的概念,通常用一個盒子(box, rectangle)來表示。mozilla通過一個叫frame的對象對盒子進行操作。frame主要的動作有三個:   構造frame, 以建立對象樹(DOM樹)reflow, 以確定對象位置,或者是調用mozilla的Layout(這裡是指源碼的實現)繪製,以便對象能顯示在螢幕上    總的來說,reflow就是載入內容樹(在HTML中就是DOM樹)和建立或更新frame結構的響應的一種過程。   那麼造成reflow的原因有: 操作DOM樹與布局有關的樣式改變改變className視窗大小調整字休大小  所以若要要提高頁面效能,其實就是避免reflow的開銷。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.