總結時間:2012-2-22javascript
1.大多數瀏覽器都使用單一進程來處理用戶界面更新和javascript腳步執行,因此同一時刻只能作其中一件事情。src屬性的js連接和<script>同樣,瀏覽器必須先花時間下載外鏈文件中的代碼,而後解析並執行,在整個過程當中,頁面渲染和用戶交互徹底阻塞。css
2.瀏覽器在解析到<body>以前,不會渲染頁面的任何部分。通常來講瀏覽器順序下載並解析執行,現代瀏覽器也容許並行下載。但還是單線程順序執行。java
3.推薦將全部的<script>標籤儘量放到<body>標籤的底部。正則表達式
4.多個js文件會增長http請求的次數,可用合併工具處理成一個文件shell
5.defer屬性用於<script>元素在onload以前,全部腳本下載以後執行。但通常只有IE才支持編程
6.無阻塞腳本的三種方式:1)<script defer>在onload以前,全部腳本下載以後執行,僅IE和FireFox3.5+支持. 2)動態腳本,用createElement("script")建立,設置爲src屬性後append到head裏。 3)XHR異步請求,將返回內容設置到所建立srcipt對象的text屬性中,append到head裏,實現異步記載。此方法不支持跨域。跨域
7.推薦先載入加載其餘js的腳本,而後用它來實現無阻塞加載腳本的目的,目前有一些異步加載的類庫,如LazyLoad,labjs,yui3等。數組
8.javascript四種數據存取位置:直接量,變量,數組元素,對象成員。前兩種的訪問速度明顯大於後兩種。js運行效率受數據存取效率的影響。瀏覽器
9.每一個函數建立時,都會初始化一個內部Scope屬性,表示函數的做用域鏈,代表此函數能夠訪問到的變量,初始化通常都是一些全局對象,咱們可稱之爲初始做用域鏈。而函數在真正運行時,會動態建立一個運行期上下文的內部對象ActiveObject,包含全部局部變量,命名參數,arguments以及this,此ActiveObject會被壓入函數Scope鏈的最高層,在函數執行完後被銷燬,咱們可稱之爲ActiveObject鏈。函數在執行過程當中解析的時候先解析此ActiveObject內的標識符,只有沒找到纔會繼續往下找函數做用域鏈裏的屬性,這就解釋了爲何訪問局部變量比訪問全局變量快。所以性能調優很大的一個技巧是對於須要屢次訪問的全局變量,如document,window等,須要用一個局部變量指代以提升訪問效率。緩存
10.改變做用域鏈的三種方式:1)使用with:使用with(object)後,在函數運行時,除了初始做用域鏈和ActiveObject鏈以外,還會壓入一個包含object對象所有屬性鏈,而且位於第一鏈,所以使用with後訪問object的屬性和方法會特別快,可是訪問原函數中變量就反而變得慢了,由於每次訪問變量也都會先今後object對象的屬性鏈中開始找。2)catch子句:異常發生後跳到catch子句中,同時會把異常對象推入一個可變對象並置於做用域的頂部,這樣作的目的固然是爲了能快速訪問到異常對象信息,可是訪問函數其餘變量就變得慢,所以不建議在catch中作過多操做。直接處理異常便可。3)閉包:常見於在函數中設置Dom元素的事件處理器,函數執行時遇到閉包代碼,此閉包被建立,同時建立的還有閉包做用域鏈,此鏈就是當前執行函數的初始做用域鏈加上ActiveObject鏈。這就解釋了爲何閉包能夠訪問所在函數的所有變量,即便所在函數已經執行完畢,可是因爲有閉包仍指引了ActiveObject鏈,所以此鏈不會被正常銷燬(IE中使用非原生javascript對象實現Dom,因爲ActiveObject中的對象在閉包指引下不能及時銷燬,會有內存泄露的危險)。同時,閉包代碼在真正執行的時候相似with,會壓入一個包含event,arguments等屬性的對象到閉包做用域鏈中,這也能夠解釋爲何在閉包代碼如事件處理代碼中咱們能夠訪問到event的屬性。
11.每一個對象經過一個內部屬性_proto_綁定到它的原型,所以屬性或方法分爲對象聲明成員和原型成員,用hasOwnProperty只訪問對象聲明成員,而用in 如 alert(name in object),對象聲明成員和原型成員都會找。搜索屬性或方法,若是存在比較深的原型鏈裏,會影響性能,爲提供性能,能夠用局部變量暫存。
12.文檔對象模型Dom自己是語言無關的,在瀏覽器裏其接口用javascript實現。Dom渲染引擎和javascript引擎是分開的,所以用js來操做dom對象必未來回調用引擎從而產生巨大的性能問題,尤爲是修改dom元素的隱顯,修改元素大小都將致使頁面被從新渲染。性能提升原則是儘可能減小js訪問dom的次數,把更多的運算在js端作完後統一發給Dom引擎處理。
13.另外一方面,當瀏覽器下載完後頁面的全部組件後,開始解析並生成Dom樹表示頁面結構以及渲染樹表示須要顯示的Dom節點。所以當咱們用js改變了Dom節點的隱顯或可見節點的大小,甚至訪問這些節點的位置,大小屬性時,瀏覽器爲提供準確的信息,都會立馬執行渲染隊列中等待處理的變化,重排位置,而後重繪出來。
14.用getElements***或document.links,forms等相似的方法取HTML集合的時候,獲得的是一種提供length特性及數字索引訪問相似數組的集合,因爲這種HTML集合與DOM實時同步,所以每次訪問哪怕是訪問length的時候都是從新執行查詢從而影響效率。
15.提供Dom訪問性能的幾個訣竅:1)用瀏覽器支持良好的innerHTML取代js的Dom節點操做通常來講都會加快速度. 2)用克隆已有元素取代建立新元素會提升一點效率. 3)用局部變量存儲HTML集合,用局部變量存儲length,用局部變量存儲須要屢次訪問的元素。4)用瀏覽器支持的children,childElementChildCount,firstElementChild等節點替換childNodes,childNodes.length,firstChild等,以過濾掉文本節點,減小判斷。(只是有些屬性IE不支持.) 。5)若是要對Dom元素進行一系列操做,能夠採用先讓元素脫離文檔流,而後操做,最後帶回文檔流。這樣作能夠減小重排和重繪元素從而提升效率。
有三種方法可使Dom元素脫離文檔流,a)隱藏元素,由於不顯示的元素不須要瀏覽器重排重繪. b)使用文檔片斷. c)將原始元素拷貝到一個新節點,在新節點操做完後替換原節點。
6)因爲每次訪問諸如偏移量,滾動位置或樣式值的時候都會是UI刷新,所以將佈局信息緩存到變量能夠減小UI刷新從而提升性能。7)減小在具體節點進行事件委託,儘可能在更高一級的節點進行,從而減小綁定事件帶來的運行開銷。
16.for in 循環對象屬性時因爲也會從原型鏈中搜索,會帶來性能問題,並且有時候咱們並不想搜索原型鏈中的屬性。
17.普通for循環應該採用倒序循環方式,即從後往前迭代,因爲任何非零數會自動轉換爲true,零爲false,這樣用倒序就減小了先得到當前index,而後和length比較的步驟,從而提升了性能。
18.if-else和switch在性能上自己並沒有差異,只是對於條件判斷較多的狀況,應該更傾向於選擇switch,或者若是判斷的鍵值之間存在某種對應關係,創建表並中表中查詢是更好的選擇。另外在if-else中的條件語句應該老是按照機率從最大到最小的排列,已減小中間沒必要要的判斷從而提升性能。
19.用Duff‘s Device減小迭代的次數提升性能。基本思想是:先遍歷總次數與8的取模次,而後遍歷總次數除於8取整的次數,每次處理八個相同的操做。如:
// dafu
var iters = Math.floor(arr.length/8);
var startIn = arr.length%8;
start = +new Date();
do{
switch(startIn){
case 0:process();
case 7:process();
case 6:process();
case 5:process();
case 4:process();
case 3:process();
case 2:process();
case 1:process();
}
startIn = 0;
}while(--iters);
20.瀏覽器的javascript引擎支持的遞歸數量與javascript調用棧大小相關,除IE外,其餘全部瀏覽器都有固定數量的調用棧限制,IE的調用棧與系統空閒內存有關。不明確或缺乏終止條件是遞歸最多見的問題,若是確實是遞歸層數量限制,能夠考慮用迭代來取代遞歸。
21.優化遞歸一個很重要的手段是運用Memoization緩存已經遞歸出來的結果,避免重複的計算。如:
function memoize(fundamental,cache){
cache = cache || {};
var shell = function(arg){
if(!cache.hasOwnProperty(arg)){
cache[arg] = fundamental(arg);
}
return cache[arg];
};
return shell;
}
22.str+="one"+"two"比str=str+"one"+"two"因爲中間過程會建立臨時變量而影響性能。而第二種作法會依次將one,two拷貝到str後。所以若是str自己比較長,就能大的提高性能,若是str不在第一位而是其餘位置,就沒有這種效果了。使用原生的concat也是一個選擇,不過比用+和+=要慢,相似數組join方式。
23.不一樣瀏覽器對字符拼接的處理不一樣:
2B方式:IE7及如下,每次鏈接一對字符串都要把它複製到一塊新分配的內存中,2B作法。不過利用數組join的方法卻是在這些瀏覽器下有很高的性能。
文藝方式:IE8:鏈接字符串只是記錄現有的字符串的引用,在真正使用字符串時纔將各個鏈接部分逐個拷貝到一個新的串中。
普通方式:其餘瀏覽器,從等號左邊第一個字符串爲準,依次加到其尾部。(FireFox普通中的文藝,在編譯過程當中若是某次鏈接時都是常量字符就提早合併好,不過實戰不強,由於通常來講要合併的都是運行期變量與常量的結合。)
24.理解正則表達式的回溯原理,避免回溯失敗。靈活運行貪婪匹配和惰性匹配。有如下幾種方法能夠提升正則性能:1)以簡單必須的資源開始.2)使用量詞模式,使他們後面的字元互斥.3)減小分支數量,縮小分支範圍.4)使用非捕獲組.5)只捕獲感興趣的文本以減小後處理.6)明確給出必須的字元,如^或$.7)使用合適的量詞,貪婪仍是惰性?
8)將正則表達式賦給變量以減小重複編譯.9)將表達式化繁爲簡。
25.去除字符串首尾空白時,能夠採用正則表達式去除開始的空白,可是若是字符串自己很長,使用\s+$去除尾空白容易有性能影響,所以能夠採用slice的方式截取,繞過長度限制。
26.瀏覽器UI線程既要執行javascript,又要負責更新用戶界面,所以若是js腳本執行的時間過長,勢必影響到界面的更新,形成死機假象,此正是單線程的悲哀。所以瀏覽器限制js腳本運行的時間是頗有必要的。IE經過在註冊表限制腳本的運行條數,默認500萬條,而其餘瀏覽器通常經過限制時間,如FireFox10秒,Safari5秒。
27.對於大段的js腳本,通常採用setTimeout和setInterval來分割時間斷定,讓js腳本讓出UI進程已作界面的更新而不是一味的執行腳本。此兩方法在被調用時開始計算延遲的時間,而後在所在方法執行結束後纔可能開始執行。
28.使用定時處理器處理數組有很大的優越性,尤爲是耗時很大的數組,固然前提是不要求同步處理,而且不要求數據處理順序。每次讓js腳本處理小於100毫秒如50毫秒左右的時間,而後用setTimeout使其處理過程暫停一小段時間如25毫秒以處理用戶界面的更新,從而避免假死的問題,及時知足用戶響應。
29.同時使用多個定時器會產生性能問題,由於只有一個UI線程。建議建立一個獨立的定時器,重複使用,每次在不至於讓瀏覽器提示腳本緩慢的前提時間下多執行一些任務以提升性能。
30.用Web Workers引入的接口可使代碼運行且不佔用瀏覽器UI線程時間,HTML5以加入。
31.Ajax請求的幾種方式:1)XHR,是最經常使用的Ajax請求,其優點是能夠靈活設置post和get,get方式經常使用於獲取數據而不是改變數據,由於其有緩存,因此屢次get提交相同的數據會有性能提高。不過IE限制URL的長度不超過2048個字符。此方式最大的缺點是不能跨域。2)動態腳本注入,能夠跨域,返回的js腳本直接執行,無需經過轉換,處理速度很快。可是隻能get方式,並且服務器必須返回可執行的js腳本且封裝在一個回調函數中,沒法讀取頭信息和響應代碼,不能及時得到服務器響應。3)Multipart XHR將多個不一樣類型的資源如js,css,image按約定的格式利用一次XHR下載,減小了HTTP請求的次數。4)Beacons圖片信標,利用設置new Image().src屬性,將簡單少許的數據傳給服務器。可監聽其load方法查看結果。
32.利用get方式請求的數據會被緩存起來,經過設置head信息,能夠緩存請求數據從而減小了重複的請求,也能夠經過存儲到變量中以url爲索引緩存。
33.高性能編程幾點建議:1)儘可能少用eval,Function,setTimeout,setInterval函數,由於他們不只會帶來雙重求值問題,並且他們每次調用時都會產生解釋器/編譯器實例。2)setTimeout,setInterval第一個參數傳入函數而不是字符串代碼。3)多使用直接量{name:dim,id:11}來建立對象而不是常見的object.name逐個建立。4)對於瀏覽器差別函數能夠採用延時加載或條件預加載的模式,在須要用到的時候才根據具體瀏覽器選擇函數,或是在頁面加載時根據瀏覽器不一樣而選擇具體的函數,千萬不要每次調用都去判斷瀏覽器類型,必定要避免重複判斷。5)多使用位運算和原生方法