這本《高性能JavaScript》講述了有關JavaScript性能優化的方方面面,主要圍繞如下幾個方面:javascript
1> 加載順序
2> 數據訪問(如怎樣的數據類型訪問最快,怎樣的做用域鏈最優)
3> DOM編程(如怎樣的方式訪問DOM元素性能是最優的)
4> 字符串和正則
5> Ajax
6> 編程實踐(性能測試工具的使用、建立與部署JavaScript應用程序、如何提高程序響應)
var script = document.createElement ("script"); script.type = "text/javascript"; script.src = "file1.js"; document.getElementsByTagName_r("head")[0].appendChild(script);
新的<script>元素加載file1.js源文件。此文件當元素添加到頁面以後馬上開始下載。此技術的重點在於:不管在何處啓動下載,文件的下載和運行都不會阻塞其餘頁面處理過程。你甚至能夠將這些代碼放在<head>部分而不會對其他部分的頁面代碼形成影響(除了用於下載文件的HTTP鏈接)。用XHR對象下載代碼,並注入到頁面中。(能夠下載不當即執行的JavaScript代碼)
var xhr = new XMLHttpRequest(); xhr.open("get", "file1.js", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { var script = document.createElement_x("script"); script.type = "text/javascript"; script.text = xhr.responseText; document.body.appendChild(script); } } }; xhr.send(null);
——推薦的非阻塞模式html
推薦的向頁面加載大量JavaScript的方法分爲兩個步驟: 第一步,包含動態加載JavaScript所需的代碼,而後加載頁面初始化所需的除JavaScript以外的部分。這部分代碼儘可能小,可能只包含loadScript()函數,它下載和運行很是迅速,不會對頁面形成很大幹擾。當初始代碼準備好以後,用它來加載其他的JavaScript。 function loadScript(url, callback) { var script = document.createElement("script"); script.type = "text/javascript"; //IE if (script.readyState) { script.onreadystatechange = function() { if (script.readyState == "loaded" || script.readyState == "complete") { script.onreadystatechange = null; callback(); } } } else { //非IE script.onload = function() { callback(); } } script.src = url; document.getElementById("head").appendChild(script); } 第二步,應用loadScript函數在body的關閉標籤以前的位置加載其他須要加載的JavaScript代碼。
第二章:數據訪問前端
在JavaScript中,數據存儲位置能夠對代碼總體性能產生重要影響。有四種數據訪問類型:直接量(字符串、數字、布爾值、對象、數組、函數、正則、null、undefined),變量(var局部變量),數組項(array[index]),對象成員(obj.propName | obj[‘propName’])。它們有不一樣的性能考慮。java
直接量和局部變量訪問速度很是快,數組項和對象成員須要更長時間。正則表達式
局部變量比域外變量快,由於它位於做用域鏈的第一個對象中。變量在做用域鏈中的位置越深,訪問所需的時間就越長。全局變量老是最慢的,由於它們老是位於做用域鏈的最後一環。算法
避免使用with表達式,由於它改變了運行期上下文的做用域鏈。並且應當當心對待try-catch表達式的catch子句,由於它具備一樣效果。編程
應用with包含的變量對象被插入到做用域鏈的前端,正因如此,訪問激活對象被擠到了第二層,會致使訪問局部變量的性能降低。故應避免。
嵌套對象成員會形成重大性能影響,儘可能少用。跨域
因爲對象成員可能包含其它成員,例如不太常見的寫法window.location.href這種模式。每遇到一個點號,JavaScript引擎就要在對象成員上執行一次解析過程。成員嵌套越深,訪問速度越慢。
通常來講,若是在同一個函數中你要屢次讀取同一個對象屬性,最好將它存入一個局部變量。以局部變量替代屬性,避免多餘的屬性查找帶來性能開銷。在處理嵌套對象成員時這點特別重要,它們會對運行速度產生難以置信的影響。
一個屬性或方法在原形鏈中的位置越深,訪問它的速度就越慢。數組
通常來講,你能夠經過這種方法提升JavaScript代碼的性能:將常用的對象成員,數組項,和域外變量存入局部變量中。而後,訪問局部變量的速度會快於那些原始變量。瀏覽器
第三章:DOM編程
DOM訪問和操做是現代網頁應用中很重要的一部分。但每次你經過橋樑從ECMAScript島到達DOM島時,都會被收取「過橋費」。爲減小DOM編程中的性能損失,請牢記如下幾點:
最小化DOM訪問,在JavaScript端作儘量多的事情。
在反覆訪問的地方使用局部變量存放DOM引用。
對於任何類型的DOM訪問,若是同一個DOM屬性或方法被訪問一次以上,最好使用一個局部變量緩存該DOM成員。
當心地處理HTML集合,由於他們表現出「存在性」,老是對底層文檔從新查詢。將集合的length屬性緩存到一個變量中,在迭代中使用這個變量。若是常常操做這個集合,能夠將集合拷貝到數組中。
當遍歷一個集合時,第一個優化是將集合引用存儲於局部變量,並在循環以外緩存length屬性,而後, 若是在循環體中屢次訪問同一個集合元素,那麼使用局部變量緩存它。
注意重繪和重排版;批量修改風格,離線操做DOM樹,緩存並減小對佈局信息的訪問。動畫中使用絕對座標。
1.重排版的狀況列舉: 1>添加或刪除可見的DOM元素 2>元素位置改版 3>元素尺寸改變(外邊距、邊框寬度、內邊距、寬度、高度) 4>內容改變(如,文本改變或圖片被另外一個不一樣尺寸的所替代) 5>瀏覽器窗口改變尺寸(如出現滾動條) 2.重排版和重繪代價昂貴,因此,提升程序響應速度一個好策略是減小此類操做發生的機會。3.爲減小發生次數,你應該將多個DOM和風格改變合併到一個批次中一次性執行。 1>當須要對DOM元素進行屢次修改時,你能夠經過如下步驟減小重繪和重排版的次數 從文檔流中摘除該元素 (1)隱藏元素,進行修改,而後再顯示它。 (2)使用一個文檔片斷在已存DOM以外建立一個子樹,而後將它拷貝到文檔中。 (3)將原始元素拷貝到一個脫離文檔的節點中,修改副本,而後覆蓋原始元素。 2>對其應用多重改變 3>將元素帶回文檔中 4.對於處理具備動畫效果的元素,須要將之脫離文檔流
使用事件託管技術最小化事件句柄數量。
第四章:算法和流程控制
正如其餘編程語言,代碼的寫法和算法選用影響JavaScript的運行時間。與其餘編程語言不一樣的是,JavaScript可用資源有限,因此優化技術更爲重要。
瀏覽器資源限制: 1>調用棧尺寸限制 2>長時間腳本限制(控制在100ms之內的響應算做好的用戶體驗)
for,while,do-while循環的性能特性類似,誰也不比誰更快或更慢。
除非你要迭代遍歷一個屬性未知的對象,不然不要使用for-in循環。
改善循環性能的最好辦法是減小每次迭代中的運算量,並減小循環迭代次數。
運行的代碼總量越大,使用這些策略所帶來的性能提高就越明顯。
第五章:字符串和正則表達式
密集的字符串操做和粗淺地編寫正則表達式多是主要性能障礙,但本章中的建議可幫助您避免常見缺陷。
當鏈接數量巨大或尺寸巨大的字符串時,數組聯合是IE7和它的早期版本上惟一具備合理性能的方法。
若是你不關心IE7和它的早期版本,數組聯合是鏈接字符串最慢的方法之一。使用簡單的+和+=取而代之,可避免(產生)沒必要要的中間字符串。
提升正則表達式效率的各類技術手段,幫助正則表達式更快地找到匹配,以及在非匹配位置上花費更少時間。
正則表達式並不老是完成工做的最佳工具,尤爲當你只是搜索一個文本字符串時。
雖然有不少方法來修整一個字符串,使用兩個簡單的正則表達式(一個用於去除頭部空格,另外一個用於去除尾部空格)提供了一個簡潔、跨瀏覽器的方法,適用於不一樣內容和長度的字符串。從字符串末尾開始循環查找第一個非空格字符,或者在一個混合應用中將此技術與正則表達式結合起來,提供了一個很好的替代方案,它不多受到字符串總體長度的影響。
第六章:響應接口
JavaScript和用戶界面更新在同一個進程內運行,同一時刻只有其中一個能夠運行。這意味着當JavaScript代碼正在運行時,用戶界面不能響應輸入,反之亦然。有效地管理UI線程就是要確保JavaScript不能運行太長時間,以避免影響用戶體驗。最後,請牢記以下幾點:
JavaScript運行時間不該該超過100毫秒。過長的運行時間致使UI更新出現可察覺的延遲,從而對總體用戶體驗產生負面影響。
JavaScript運行期間,瀏覽器響應用戶交互的行爲存在差別。不管如何,JavaScript長時間運行將致使用戶體驗混亂和脫節。
定時器可用於安排代碼推遲執行,它使得你能夠將長運行腳本分解成一系列較小的任務。
網頁工人線程是新式瀏覽器才支持的特性,它容許你在UI線程以外運行JavaScript代碼而避免鎖定UI。
網頁應用程序越複雜,積極主動地管理UI線程就越顯得重要。沒有什麼JavaScript代碼能夠重要到容許影響用戶體驗的程度。
第七章:Ajax
高性能Ajax包括:知道你項目的具體需求,選擇正確的數據格式和與之相配的傳輸技術。
做爲數據格式,純文本和HTML是高度限制的,但它們可節省客戶端的CPU週期。XML被普遍應用廣泛支持,但它很是冗長且解析緩慢。JSON是輕量級的,解析迅速(做爲本地代碼而不是字符串),交互性與XML至關。字符分隔的自定義格式很是輕量,在大量數據集解析時速度最快,但須要編寫額外的程序在服務器端構造格式,並在客戶端解析。
當從頁面域請求數據時,XHR提供最完善的控制和靈活性,儘管它將全部傳入數據視爲一個字符串,這有可能下降解析速度。另外一方面,動態腳本標籤插入技術容許跨域請求和本地運行JavaScript和JSON,雖然它的接口不夠安全,並且不能讀取信息頭或響應報文代碼。多部分XHR可減小請求的數量,可在一次響應中處理不一樣的文件類型,儘管它不能緩存收到的響應報文。當發送數據時,圖像燈標是最簡單和最有效的方法。XHR也可用POST方法發送大量數據。
減小請求數量,可經過JavaScript和CSS文件打包,或者使用MXHR。
縮短頁面的加載時間,在頁面其它內容加載以後,使用Ajax獲取少許重要文件。
Ajax是提高你網站潛在性能之最大的改進區域之一,由於不少網站大量使用異步請求,又由於它提供了許多不相關問題的解決方案,這些問題諸如,須要加載太多資源。對XHR的創造性應用是如此的不同凡響,它不是呆滯不友好的界面,而是響應迅速且高效的代名詞;它不會引發用戶的憎恨,誰見了它都會愛上它。
第八章:編程實踐
JavaScript提出了一些獨特的性能挑戰,關係到你組織代碼的方法。網頁應用變得愈來愈高級,包含的JavaScript代碼愈來愈多,出現了一些模式和反模式。請牢記如下編程經驗:
經過避免使用eval_r()和Function()構造器避免二次評估。此外,給setTimeout()和setInterval()傳遞函數參數而不是字符串參數。
建立新對象和數組時使用對象直接量和數組直接量。它們比非直接量形式建立和初始化更快。
避免重複進行相同工做。當須要檢測瀏覽器時,使用延遲加載或條件預加載。
//之前的方法 function addHandler(target, eventType, handler) { if (target.addEventListener) {//DOM2 Events target.addEventListener(eventType, handler, false); } else {//IE target.attachEvent("on" + eventType, handler); } } function removeHandler(target, eventType, handler) { if (target.removeEventListener) {//DOM2 Events target.removeEventListener(eventType, handler, false); } else {//IE target.detachEvent("on" + eventType, handler); } } //延遲加載 function addHandler(target, eventType, handler){ debugger; //overwrite the existing function if (target.addEventListener){ //DOM2 Events addHandler = function(target, eventType, handler){ target.addEventListener(eventType, handler, false); }; } else { //IE addHandler = function(target, eventType, handler){ target.attachEvent("on" + eventType, handler); }; } //call the new function addHandler(target, eventType, handler); } function removeHandler(target, eventType, handler){ //overwrite the existing function if (target.removeEventListener){ //DOM2 Events removeHandler = function(target, eventType, handler) { target.addEventListener(eventType, handler, false); } } else { //IE removeHandler = function(target, eventType, handler) { target.detachEvent("on" + eventType, handler); }; } //call the new function removeHandler(target, eventType, handler); } //條件預加載 var addHandler = document.body.addEventListener ? function(target, eventType, handler) { target.addEventListener(eventType, handler, false); } : function(target, eventType, handler) { target.attachEvent("on" + eventType, handler); }; var removeHandler = document.body.removeEventListener ? function(target, eventType, handler) { target.removeEventListener(eventType, handler, false); } : function(target, eventType, handler) { target.detachEvent("on" + eventType, handler); };
當執行數學運算時,考慮使用位操做,它直接在數字底層進行操做,性能最優。
原生方法老是比JavaScript寫的東西要快。儘可能使用原生方法。
本書涵蓋了不少技術和方法,若是將這些優化應用在那些常常運行的代碼上,你將會看到巨大的性能提高。
第九章:建立並部署高性能JavaScript應用程序
開發和部署過程對基於JavaScript的應用程序能夠產生巨大影響,最重要的幾個步驟以下:
合併JavaScript文件,減小HTTP請求的數量。
使用YUI壓縮器緊湊處理JavaScript文件。
以壓縮形式提供JavaScript文件(gzip編碼)。
經過設置HTTP響應報文頭使JavaScript文件可緩存,經過向文件名附加時間戳解決緩存問題。
使用內容傳遞網絡(CDN)提供JavaScript文件,CDN不只能夠提升性能,它還能夠爲你管理壓縮和緩存。
全部這些步驟應當自動完成,不管是使用公開的開發工具諸如Apache Ant,仍是使用自定義的開發工具以實現特定需求。若是你使這些開發工具爲你服務,你能夠極大改善那些大量使用JavaScript代碼的網頁應用或網站的性能。
第十章:工具
當網頁或應用程序變慢時,分析網上傳來的資源,分析腳本的運行性能,使你可以集中精力在那些須要努力優化的地方。
使用網絡分析器找出加載腳本和其它頁面資源的瓶頸所在,這有助於決定哪些腳本須要延遲加載,或者進行進一步分析。
傳統的智慧告訴咱們應儘可能減小HTTP請求的數量,儘可能延遲加載腳本以使頁面渲染速度更快,向用戶提供更好的總體體驗。
使用性能分析器找出腳本運行時速度慢的部分,檢查每一個函數所花費的時間,以及函數被調用的次數,經過調用棧自身提供的一些線索來找出哪些地方應當努力優化。
雖然花費時間和調用次數一般是數據中最有價值的點,仍是應當仔細察看函數的調用過程,可能發現其它優化方法。
這些工具在那些現代代碼所要運行的編程環境中再也不神祕。在開始優化工做以前使用它們,確保開發時間用在解決問題的刀刃上。
傳個logo做記念——
原文英文及翻譯:《高性能JavaScript》