我以爲優化javascript是一門高深的學問,在這裏也只能站在前人的肩膀上,說一些我淺顯的認識,更但願的是拋鑽引玉,若有不對,敬請斧正。javascript
首先,要認識到是,優化js的關鍵之處在於,優化它的運行速度,以此爲切入點。css
javascript的優化原則是:二八原則html
知足考量大多數狀況,而遇到極端狀況,有能力則兼顧之,學會放棄,適當取捨;前端
緣由是,影響用戶的體驗很重要的因素之一響應時間java
固然js優化只是提高響應時間須要改善的方面的衆多之一,前端優化知識何其之深,只有深刻了解,纔會驚訝於前端所須要掌握的知識竟是如此之多,固然無形之中也會給人壓力||動力。扯遠了,可是仍是想力薦兩篇關於前端優化的blog:前端工程打開速度優化的按部就班總結 和 web前端優化最佳實踐及工具。web
繼續正題,ok,那麼知道了目標是提高響應時間,加快運行速度,那麼具體有哪些可行的方案呢:數組
管理做用域瀏覽器
舉個板栗:緩存
var foo = 1;
function test(){
//對變量foo進行一系列操做
}
function test2(){
var foo = 1;
//對變量foo進行一系列操做
}
也就是說,局部變量存在於活動對象中,解析器只需查找做用域中的單個對象。app
在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)。做用域鏈會經過下面兩個步驟來創建,用於進行標識符解析。在執行JavaScript代碼的過程當中,當遇到一個標識符,就會根據標識符的名稱,在執行上下文(Execution Context)的做用域鏈中進行搜索。從做用域鏈的第一個對象(該函數的Activation Object對象)開始,若是沒有找到,就搜索做用域鏈中的下一個對象,如此往復,直到找到了標識符的定義。若是在搜索完做用域中的最後一個對象,也就是全局對象(Global Object)之後也沒有找到,則會拋出一個錯誤,提示用戶該變量未定義(undefined)。這是在ECMA-262標準中描述的函數執行模型和標識符解析(Identifier Resolution)的過程,事實證實,大部分的JavaScript引擎確實也是這樣實現的。須要注意的是,ECMA-262並無強制要求採用這種結構,只是對這部分功能加以描述而已。 瞭解標識符解析(Identifier Resolution)的過程之後,咱們就能明白爲何局部變量的解析速度要比其餘做用域的變量快,主要是因爲搜索過程被大幅縮短了。
- 首先將函數對象[[Scope]]內部屬性中的對象,按順序複製到做用域鏈中。
- 其次,在函數執行時,會建立一個新的Activation Object對象,這個對象中包含了this、參數(arguments)、局部變量(包括命名的參數)的定義,這個Activation Object對象會被置於做用域鏈的最前面。
也就是:當標識符解析的過程須要進行深度搜索時,會伴隨性能損失,並且性能損失的程度會隨着標識符深度的增長而遞增。
數據操做
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. 增刪查改
2. 指明操做DOM的context
context.getElementsByTagName()
3. 拆分方法,一個方法解決一件事
拆分功能,讓一個方法只作一件事,經過不斷地調用方法來實現複雜功能,可是,這些簡單方法要避免相互交叉調用。
Be Lazy(使腳本儘量少地運行,或者不運行。)
流控制
if(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
else{
}
原則:
var foo = 0;
if(foo == false){ //隱式轉換
...
}
function recurse(){
recurse();
}
recurse(); //又是一個悲劇,會報錯,無限遞歸了
Reflow
何爲reflow,便是:在CSS規範中有一個渲染對象的概念,一般用一個盒子(box, rectangle)來表示。mozilla經過一個叫frame的對象對盒子進行操做。frame主要的動做有三個:
總的來講,reflow就是載入內容樹(在HTML中就是DOM樹)和建立或更新frame結構的響應的一種過程。
那麼形成reflow的緣由有:
因此若要要提升頁面性能,其實就是避免reflow的開銷,可是形成reflow的緣由有時候是爲了完成交互效果而不可避免的,因此不能說徹底避免,只能盡最大限度的去減小,這就如我開頭而言的二八原則。
如下是一些簡單的指導方針能夠幫助你頁面上的迴流(reflow)減到最小。