最近在研讀《高性能JavaScript》,在此作些簡單記錄。示例代碼可在此處查看到。html
1)DOM和JavaScriptgit
文檔對象模型(DOM)是一個獨立於語言的,用於操做XML和HTML文檔的程序接口(API)。正則表達式
瀏覽器一般會把DOM和JavaScript獨立實現。例如Chrome中使用Webkit的WebCore庫渲染頁面,用V8做爲JavaScript引擎。編程
訪問DOM天生就慢,將DOM和JavaScript比喻爲兩個島嶼,兩處同行要收過橋費,ECMAScript訪問DOM的次數越多,過橋費越貴,所以推薦的作法是儘量減小過橋的次數。設計模式
2)性能測試跨域
下圖是對兩段代碼的性能測試,可在此處查看在線性能測試。數組
1. 第一段每一次循環都直接用DOM賦值瀏覽器
2. 第二段是先將內容緩存到局部變量中,最後使用一次DOM賦值。緩存
測試結果以每秒鐘執行測試代碼的次數(Ops/sec
)顯示,這個數值越大越好。
除了這個結果外,同時會顯示測試過程當中的統計偏差,以及相對最好的慢了多少(%),統計偏差也是很是重要的指標。
有時候爲了獲得須要的元素列表,須要組合調用getElementById等並遍歷返回的節點,但這種繁密的過程效率低下。
使用CSS選擇器是一種定位節點的便利途徑,querySelectorAll就是DOM的原生方法。
//DOM組合API var elements = document.getElementById('menu').getElementsByTagName('a'); //替換爲簡便的CSS選擇器 var elements = document.querySelectorAll('#menu a');
在《CSS動畫與JavaScript動畫》中層提到過頁面渲染的通常過程爲JavaScript > 計算樣式 > 佈局 > 繪製 > 渲染層合併。
Layout(重排)和Paint(重繪)是整個環節中最爲耗時的兩環,因此咱們儘可能避免着這兩個環節。
當DOM的變化影響了元素的幾何屬性(寬和高)將會發生重排
(reflow);
完成重排後,瀏覽器會從新繪製受影響的部分到屏幕中,此過程爲重繪
(repaint)。
1)重排什麼時候發生
1. 添加或刪除可見的DOM元素
2. 元素位置改變
3. 元素尺寸改變(包括外邊距、內邊距、邊框寬度、寬、高等屬性)
4. 內容改變,例如文本改變或圖片被不一樣尺寸的替換掉。
5. 頁面渲染器初始化。
6. 瀏覽器窗口尺寸改變。
2)批量執行重排
下面代碼看上去會重排3次,但其實只會重排1次,大多數瀏覽器經過隊列化修改和批量顯示優化重排版過程。
//渲染樹變化的排隊和刷新 var ele = document.getElementById('myDiv'); ele.style.borderLeft = '1px'; ele.style.borderRight = '2px'; ele.style.padding = '5px';
但下列操做將會強迫隊列刷新並要求全部計劃改變的部分馬上應用:
offsetTop, offsetLeft, offsetWidth, offsetHeight scrollTop, scrollLeft, scrollWidth, scrollHeight clientTop, clientLeft, clientWidth, clientHeight getComputedStyle() (currentStyle in IE)(在 IE 中此函數稱爲 currentStyle)
像offsetHeight屬性須要返回最新的佈局信息,所以瀏覽器不得不執行渲染隊列中的「待處理變化」並觸發重排以返回正確的值。
對於尺寸座標相關的信息能夠參考《JavaScript中尺寸、座標》。
3)最小化重繪和重排
1. cssText和class
cssText能夠一次設置多個CSS屬性。class也能夠一次性設置,而且更清晰,更易於維護,但有前提條件,就是不依賴於運行邏輯和計算的狀況。
// cssText ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;'; // class ele.className = 'active';
在《JavaScript特性(attribute)、屬性(property)和樣式(style)》詳細介紹了CSS相關的JS操做。
2. 批量修改DOM
2.1 隱藏元素display:none
,應用修改,從新顯示display:block
。
2.2 使用文檔片斷fragment
,在片斷上操做節點,再拷貝迴文檔。
//文檔片斷(fragment) var fragment = document.createDocumentFragment(); var li = document.createElement('li'); li.innerHTML = 'banana'; fragment.appendChild(li); document.getElementById('fruit').appendChild(fragment);
2.3 將原始元素拷貝到一個脫離文檔的節點中(例如position:absolute),修改副本,完成後再替換原始元素。
1)第一章 加載和執行
將<script>標籤放到頁面底部,也就是</body>閉合標籤以前。
多種無阻塞下載JavaScript的方法:
1. 使用<script>標籤的defer
屬性。頁面解析到<script>標籤時開始下載,但並不會執行,直到DOM加載完(onload
事件觸發前)。
2. 動態建立<script>元素來下載並執行代碼。不管在什麼時候啓動下載,文件的下載和執行過程不會阻塞頁面其它進程,但返回的代碼一般會馬上執行。
3. 使用XHR對象下載JavaScript代碼並注入頁面中。優勢是下載後不會自動執行,全部主流瀏覽器都支持,但不能跨域下載。
2)第二章 數據存取
1. 每遇到一個變量,就會經歷一次標識符解析的過程,以決定在哪裏獲取和存儲數據。在執行環境的做用域中,標識符所在的位置越深,讀寫速度也就越慢。所以函數中讀寫局部變量是最快的,讀寫全局變量是最慢的。
2. 因爲對象成員可能包含其它成員,例如window.location.href
。每次遇到點操做符,嵌套成員會致使JavaScript引擎搜索所有對象成員。對象成員嵌套越深,讀取速度越慢。location.href
就比window.location.href
快。
3)第五章 字符串與正則表達式
str = str + "one";//性能高 str = "one" + str;//性能差
1. 除IE外,瀏覽器會簡單的將第二個字符串拷貝到第一個的後面,若是變量str很大的話,就會出現性能損耗(內存佔用)就會很高。
2. 正則優化包括:減小分支數量,縮小分支範圍;使用非捕獲數組;只捕獲感興趣的文本以減小後期處理;使用合適的量詞;化繁爲簡,分解複雜的正則;
4)第六章 快速響應的用戶界面
使用定時器讓出時間片斷,分割大型任務,在文章《JavaScript定時器分析》中有具體分析。
5)第七章 Ajax
Mutipart XHR
容許客戶端只用一個HTTP請求就能夠從服務器向客戶端傳送多個資源。
將資源文件(CSS、HTML、JavaScript、Base64編碼圖片)打包成一個由雙方約定的字符串分隔符,發送到客戶端。
而後用JavaScript代碼處理這個字符串,根據「mime-type」類型和傳入的頭信息解析每一個資源。
6)第九章 構建並部署高性能JavaScript應用
1. 合併JavaScript文件以減小HTTP請求數。
2. 壓縮JavaScript文件。
3. 在服務器端壓縮JavaScript文件(Gzip編碼)。
4. 正確設置HTTP響應頭來緩存JavaScript文件,經過向文件名增長時間戳避免緩存問題。
5. 使用CDN(Content Delivery Network)提供JavaScript文件。
第八章 編程實踐內容比較多,單獨令出來做爲一節。
1)使用Object/Array直接量
代碼例下:
var myObject = { name: "pwstrick", age: 29 }; var myArr = ["pwstrick", 29];
2)避免重複工做
也就是惰性模式。減小每次代碼執行時的重複性分支判斷,經過對對象重定義來屏蔽原對象中的分支判斷。
惰性模式分爲兩種:第一種文件加載後當即執行對象方法來重定義,第二種是當第一次使用方法對象時來重定義。可參考在線demo代碼。
在文章《JavaScript設計模式》中有更多的設計模式介紹。
3)位運算
1. 用位運算取代純數學操做,好比對2取模digit%2
能夠判斷偶數與奇數。
2. 位掩碼技術,使用單個數字的每一位來判斷選項是否成立。掩碼中每一個選項的值都是2的冪。例如:
var OPTION_A = 1, OPTION_B = 2, OPTION_C = 4, OPTION_D = 8, OPTION_E = 16; //用按位或運算建立一個數字來包含多個設置選項 var options = OPTION_A | OPTION_C | OPTION_D; //接下來能夠用按位與操做來判斷給定的選項是否可用 //選項A是否在列表中 if(options & OPTION_A) { //... }
3. 用按位左移(<<)作乘法,用按位右移作除法(>>),例如digit*2
能夠替換成digit<<2
。
4)原生方法
不管你的代碼如何優化,都比不上JavaScript引擎提供的原生方法快。
1. 數學運算用內置的Math對象中提供的方法。
2. 用原生的CSS選擇器查找DOM節點,querySelector或querySelectorAll。