前端工程優化:javascript的優化小結


   我覺得優化javascript是一門高深的學問,在這里也只能站在前人的肩膀上,說一些我淺顯的認識,更希望的是拋鑽引玉,如有不對,敬請斧正。

  首先,要認識到是,優化js的關鍵之處在於,優化它的運行速度,以此為切入點。

  javascript的優化原則是:二八原則

  

  滿足考量大多數情況,而遇到極端情況,有能力則兼顧之,學會放棄,適當取舍;

  

  原因是,影響用戶的體驗很重要的因素之一響應時間

  • 0.1s: 用戶覺得很流暢
  • 1.0s: 用戶的操作可能偶爾受到影響,並且用戶已經能感覺到有些不流暢
  • 10s : 對用戶的影響比較嚴重,需要相應的進度提示。用戶也會有一些沮喪   (//我覺得10s太寬泛了,通常而言2s以上就受不了了)

 

  當然js優化只是提升響應時間需要改善的方面的眾多之一,前端優化知識何其之深,只有深入了解,才會驚訝於前端所需要掌握的知識竟是如此之多,當然無形之中也會給人壓力||動力。扯遠了,但是還是想力薦兩篇關於前端優化的blog:前端工程打開速度優化的循序漸進總結 和 web前端優化最佳實踐及工具

  繼續正題,ok,那么知道了目標是提升響應時間,加快運行速度,那么具體有哪些可行的方案呢:

  1. 管理作用域
  2. 操作數據
  3. 流控制
  4. Reflow
  5. DOM操作
  6. 長時間運行的腳本處理

  

  

  管理作用域

  舉個板栗:

  

var foo = 1;
function test(){
	//對變量foo進行一系列操作
}

function test2(){
	var foo = 1;
	 //對變量foo進行一系列操作
}

 

  也就是說,局部變量存在於活動對象中,解析器只需查找作用域中的單個對象。

  在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)。作用域鏈會通過下面兩個步驟來建立,用於進行標識符解析。
  1. 首先將函數對象[[Scope]]內部屬性中的對象,按順序復制到作用域鏈中。
  2. 其次,在函數執行時,會創建一個新的Activation Object對象,這個對象中包含了this、參數(arguments)、局部變量(包括命名的參數)的定義,這個Activation Object對象會被置於作用域鏈的最前面。
  在執行JavaScript代碼的過程中,當遇到一個標識符,就會根據標識符的名稱,在執行上下文(Execution Context)的作用域鏈中進行搜索。從作用域鏈的第一個對象(該函數的Activation Object對象)開始,如果沒有找到,就搜索作用域鏈中的下一個對象,如此往復,直到找到了標識符的定義。如果在搜索完作用域中的最后一個對象,也就是全局對象(Global Object)以后也沒有找到,則會拋出一個錯誤,提示用戶該變量未定義(undefined)。這是在ECMA-262標准中描述的函數執行模型和標識符解析(Identifier Resolution)的過程,事實證明,大部分的JavaScript引擎確實也是這樣實現的。需要注意的是,ECMA-262並沒有強制要求采用這種結構,只是對這部分功能加以描述而已。  了解標識符解析(Identifier Resolution)的過程以后,我們就能明白為什么局部變量的解析速度要比其他作用域的變量快,主要是由於搜索過程被大幅縮短了。

  也就是:當標識符解析的過程需要進行深度搜索時,會伴隨性能損失,而且性能損失的程度會隨着標識符深度的增加而遞增。

 

 

  數據操作

  1.  使用局部變量,它是最快的

  obj.name比obj.xxx.name訪問更快,訪問屬性的速度,與其在對象中的深度有關。“ . ”操作的次數直接影響着訪問對象屬性的耗時。

  2.  緩存頻繁使用的對象、數組及相關的屬性值

    

function process(data){
	var count = data.count;
	if (count > 0){
		for(var i = 0; i < count ; i++){
			processData(data.item[i]);
		}		
	}
}

  3.  不直接操作NodeList,將其轉換成靜態數組后再使用 

  方法: Array.prototype.slice.call() => 標准瀏覽器

      逐個拷貝到一個新數組中 => For IE

    需要注意的是,遍歷NodeList時,不做對當前NodeList相關結構有影響的DOM操作,並且如之前所提到的,要緩存一些頻繁使用到的屬性值,以免發生不必要的悲劇。板栗:

  

	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);
	}

  上面的代碼最后運行會報錯,原因通過不斷地往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(使腳本盡可能少地運行,或者不運行。)  

  1.  短路表達式應用:如 a && b || c
  2.   基於事件去寫相應的處理方法
  3.  惰性函數

  

 

  流控制

  

if(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
else{
}

  原則:

  • 在if語句中,將經常會發生的條件,放在靠上的位置
  • if的條件為連續的區間時,可以使用二分法的方式來拆分
  • 較多離散值的判斷,可以使用switch來替代
  • 使用數組查詢的方式
  • 要注意隱式的類型轉換
  • 小心遞歸  
var foo = 0;
	
    if(foo == false){ //隱式轉換
		...
     } 

  

function recurse(){
	recurse();
}
recurse(); //又是一個悲劇,會報錯,無限遞歸了

  

 

  Reflow

  何為reflow,即是:在CSS規范中有一個渲染對象的概念,通常用一個盒子(box, rectangle)來表示。mozilla通過一個叫frame的對象對盒子進行操作。frame主要的動作有三個:

 

  • 構造frame, 以建立對象樹(DOM樹)
  • reflow, 以確定對象位置,或者是調用mozilla的Layout(這里是指源碼的實現)
  • 繪制,以便對象能顯示在屏幕上

 

  總的來說,reflow就是載入內容樹(在HTML中就是DOM樹)和創建或更新frame結構的響應的一種過程。

  那么造成reflow的原因有:

  • 操作DOM樹
  • 與布局有關的樣式改變
  • 改變className
  • 窗口大小調整
  • 字休大小

  所以若要要提高頁面性能,其實就是避免reflow的開銷,但是造成reflow的原因有時候是為了完成交互效果而不可避免的,所以不能說完全避免,只能盡最大限度的去減少,這就如我開頭而言的二八原則。

 

  以下是一些簡單的指導方針可以幫助你頁面上的回流(reflow)減到最小。

 

  • 減少不必要的DOM深度。因為無論你改變DOM節點樹上任何一個層級都會影響節點樹的每個層級——從根結點一直到修改的子節點。不必要的節點深度將導致執行回流時花費更多的時間。
  • 精簡css,去除沒有用處的css
  • 如果你想讓復雜的表現發生改變,例如動畫效果,那么請在這個流動線之外實現它。使用position-absolute或position-fixed來實現它。
  • 避免不必要的復雜的css選擇符,尤其是使用子選擇器,或消耗更多的CPU去做選擇器匹配。

  

    轉載注明出處:http://www.cnblogs.com/coco1s/p/3946435.html

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM