高性能JavaScript--讀書筆記
一:加載和執行
1.<script>標簽放在頁面底部,</body>閉合標簽之前,這能確保在腳本執行前頁面已經完成渲染。
2.合並腳本。頁面中<script>標簽越少加載越快。
3.無阻塞下載腳本:defer(延遲加載)、async(異步加載)、動態創建<script>元素下載並執行、使用XHR對象下載js代碼並註入頁面中。
二、數據存取
四種基本數據存儲方式:字面量(基本類型:字符串、數字、布爾值、對象、數組、函數、正則式,直接賦值不用new的等式右邊的值)、變量、數組項、對象成員。
1.訪問字面量和局部變量最快,訪問數組和對象成員相對較慢。
2.局部變量在作用域鏈的起始位置,因此訪問局部變量比訪問跨作用域變量更快。
3.避免使用with語句,它會改變執行環境作用域鏈,變量深度有變。
4.嵌套的對象成員少用,會明顯影響性能。執行location.href 比Window.location.href快。
5.屬性或方法在原型鏈中的位置越深,訪問它速度越慢。
通常來說,可以把常用的對象成員、數組元素、跨域變量保存在局部變量中來改善js性能,因為局部變量訪問速度更快。
對象字面量比new Object()更高效:{}字面量可立即求值,而new Object()本質上是方法調用,所以需要作用域解析,找到同名函數後創建對象。
對象字面量vs構造函數創建對象對比(字面量優勢):
a.它的代碼量更少,更易讀;
b.它可以強調對象就是一個簡單的可變的散列表,而不必一定派生自某個類;
c.對象字面量運行速度更快,因為它們可以在解析的時候被優化:它們不需要"作用域解析(scope resolution)";因為存在我們創建了一個同名的構造函數Object()的可能,當我們調用Object()的時候,解析器需要順著作用域鏈從當前作用域開始查找,如果在當前作用域找到了名為Object()的函數就執行,如果沒找到,就繼續順著作用域鏈往上找,直到找到全局Object()構造函數為止
d.Object()構造函數可以接收參數,通過這個參數可以把對象實例的創建過程委托給另一個內置構造函數,並返回另外一個對象實例,而這往往不是你想要的。
//對象直接量定義 var o = { propertyfun: function ([parameters]) {}, get property() {}, set property(value) {}, };
通過對象字面量和構造函數創建對象的區別: https://segmentfault.com/a/1190000008462406
三:DOM操作
1.最小化DOM訪問次數,盡可能在JS端處理。(DOM渲染引擎和js引擎是獨立的,交互消耗性能)如遍歷.innerHTML+=‘a’賦值時,可先把’aa..’內容合並然後一次性賦值。
2.如果需要多次訪問某個DOM節點,使用局部變量存儲它的引用。
3.小心處理HTML集合(如document.images,document.links 返回a元素),因為它實時聯系著底層文檔。如果需要遍歷時,集合長度緩存到變量,如果需要經常操作集合,可以復制到一個數組中。DOM標準中HTML集合以一種“假定實時態”實時存在,當底層文檔對象更新時,它也會自動更新,即使獲取集合元素個數也會執行詢問過程。
4.如果可能的話,使用速度更快的API,如queryselectorall(返回NodeList ,返回的不是HTML集合,因此返回的節點不會對應實時的文檔結構。而 document.getElementById()是HTML集合,需要把它拷貝到數組中才能得到類似qall類似的靜態列表)和firstElementChild
5.註意重繪(非幾何屬性變化後引起。)和重排(幾何屬性和頁面布局改變時。重排優化:通過隊列化修改並批量執行來優化重排過程,但部分方法會強制刷新渲染隊列,如offsetTop偏移量/scrollLeft滾動位置/clientWidth系列和計算出樣式值getComputedStyle/currentStyle):最小化重排和重繪“”合並多次對DOM和樣式的修改;批量修改樣式時,“離線”操作Dom樹(脫離文檔:display,文檔片段,復制節點),使用緩存布局信息(偏移量等),並減少訪問局部信息的次數。
6.動畫中使用絕對定位(減少刷新渲染隊列),使用拖放代理()
7.使用事件委托(target/scrElement)來減少事件處理器的數量。
四:算法和流程控制
1. 在判斷條件較多時,使用查找表比if-else和switch更快。
2. 瀏覽器調用棧大小限制了遞歸算法在js中的應用,棧溢出會導致代碼中斷。可改為叠代算法或Memoization(緩存運算結果的方法)來避免重復計算。
五:字符串和正則表達式
1.不考慮IE7數組項合並是比較慢的字符串連接方法,可用簡單的+和+=操作符替代,避免不必要的中間字符串(IE除外,其他瀏覽器器會嘗試為表達式左側的字符串分配更多的內存,然後簡單將第二個字符串拷貝到它的末尾。如果循環中基礎字符串位於最左端,可以避免重復拷貝一個逐漸變大的基礎字符串例: str +=’one’;str +=’two’可使用str=str+’one’+’two’提升性能,str作為基礎,每次給它附加一個字符串。.但如果str =’one’+str+’two’優化失效)。
2.正則式處理好回溯問題
六:快速響應的用戶界面
1.任何js任務都不應當執行超過100毫秒。過長運行時間導致UI更新出現明顯遲緩影響用戶體驗。
2.可使用定時器分解大任務為小任務後執行,或使用WebWorker,它允許在UI線程外執行js代碼,從而避免鎖定UI。
七:快速響應的用戶界面
1.選擇正確的數據格式:純文本和html只適用於特定場合,可節省客戶端CPU周期;XML支持良好但它十分笨重且解析緩慢;JSON輕量級,解析速度快;字符分隔自定義格式十分輕量級,在解析大量數據集時非常快,但需要編寫額外的服務端構造程序,並在客戶端解析。
2.數據請求方式
XMLHttpRequest(XHR):不能從外域請求數據,從服務器端返回的數據被當做字符串或XML,這意味著處理大量數據將會很慢。不改變服務器狀態,只獲取數據的請求使用get,使用Get請求的數據會被緩存起來。參數超過2048個字符可使用POST請求。
Dynamic script tag insertion動態腳本註入:可跨域請求 var el=document.createElement(‘script‘);el.src=’’;(‘head’)[0].appendChild(el);
缺點:不能設置請求頭,只能用get方式,不能設置請求超時處理或重試,是否失敗也不一定知道。響應消息作為腳本標簽的源碼,它必須是可執行的js代碼。
Iframes
Comet:
Beacons:類似動態腳本註入,使用js創建Image對象,把src設置為服務器上的腳本url。適合發送少量數據返回信息不太重要
var beacon= new Image(); beacon.src=url+’?key1=value1’; beacon.onload=function(){ if(this.width==1){ //如約定返回圖片寬度為1表示成功2表示重試,如果無需返回數據返回一個204 no content 它將阻止客戶端繼續等待永遠不會到來的消息正文 }else if(this.width==2){ //2否則重試 }} beacon.onerror=function(){}
Multipart XHR:允許客戶端只用一個http請求就可以從服務端向客戶端傳送多個資源。數據不能緩存
註:任何數據類型都可以被js作為字符串發送。
3.減少請求數,可通過合並js、css文件或使用MXHR;
4.縮短頁面加載時間,頁面主要內容加載完成後,用ajax獲取次要文件
八:編程實踐
1.避免使用eval和Funciton構造器來避免雙重求值(在js代碼中執行另一段代碼,此時首先會以正常方式求值,然後在執行過程中對包含字符串中的代碼發起另一個求值運算)帶來的性能消耗。給setTimeout和setInterval傳遞函數而不是字符串作為參數
2.避免重復工作(事件處理的兼容函數)。當需要檢測瀏覽器時,可使用延遲加載(腳本調用時檢測)和條件預加載(腳本加載期間提前檢測)。
延遲加載:
function addHandler(target,eventtype,handler){ if(target.addEventListener){ addHandler=function(target,eventtype,handler){ target.addEventListener(eventtype,handler,false) } }else{ addHandler = function (target,eventtype,handler){ target.attachEvent(“on”+eventtype,handler) } } addHandler(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) }
3.在進行數學計算時考慮使用自己操作數字的二進制形式的位運算
4.盡量使用原生方法。
九:構建並部署高性能js應用
1.合並js文件減少http請求量
2.壓縮js文件
3.在服務端壓縮js文件(Gzip編碼)
4.通過正確設置http響應頭來緩存js文件,通過改變文件名的方式強制瀏覽器重新加載指定文件
5.使用CDN(content Delivery networtk)內容分發網絡提供js文件;CDN不僅可以提升性能,還能管理文件的壓縮與緩存。
十:構建並部署高性能js應用
1.使用網絡分析工具找出加載腳本和頁面中其他資源的瓶頸,幫助決定哪些腳本需要延遲加載等
2.盡量減少http請求數,把腳本盡可能延遲加載
3.使用性能分析工具找出腳本運行過程中速度較慢的地方,檢查每個函數所消耗的時間以及函數被調用的次數,通過調用棧自身提供的線索來找出需要集中精力優化的地方
其他:
應用實踐中代碼優化,提取重用函數,重用組件等。
高性能JavaScript--讀書筆記