【讀書筆記】《高性能JavaScript》

缺陷

這本書是2010年出版的,這本書談性能是有時效性的,如今立刻就2018年了,這幾年前端發展的速度是飛快的,書裏面還有一些內容考慮IE六、七、8的東西,卻不知如今這些都已經再也不考慮了,因此不可避免的有一些知識是比較老的。有些解決方法如今已經不是最好的解決方式,好比工具那一章。css

前言

總的來講,這本書總體給出的性能優化建議,以及做者耐心的實踐,對咱們開發優化的啓發和幫助仍是很大的,由於它裏邊的不少知識,都是做者經過實踐總結出來的,都是經驗的積累,這在通常的教科書上是學不到的。特別是對於js基礎比較差一點的,裏面有不少知識點儘管在如今仍是很是有必要的。html

下面我就將各章節的一些重要的知識點總結寫出來,爭取將乾貨都提取出來前端

本文首發於個人我的blog:obkoro1.comwebpack


正文

第一章-加載和執行

  1. js的阻塞特性:

當瀏覽器在執行js代碼的時候,不能同時作其餘事情。(界面ui線程和js線程用的是同一進程,因此js執行越久,網頁的響應時間越長。)nginx

  1. 腳本的位置

若是把腳本<script>放在<head>中,頁面會等js文件所有下載並執行完成後纔開始渲染,在這些文件下載和執行的過程當中:會致使訪問網站的時候有明顯的延遲,表現爲:頁面空白。es6

性能提高:推薦將全部的<script>標籤儘量的放到<body>標籤的底部,優先渲染頁面,減小頁面空白時間。web

  1. 組件腳本。

每一個<script>標籤初始下載的時候都會阻塞頁面的渲染。性能提高作法: 減小內嵌腳本:減小內嵌的<script>標籤,將代碼寫在一個標籤中。ajax

合併外鏈的js文件:http請求會帶來額外的性能開銷,栗子:下載一個100KB的js文件比下載4個25kb的js文件更快。具體操做方法自行搜索。正則表達式

  1. 無阻塞腳本的方法

script標籤的aync屬性算法

async 屬性規定一旦腳本可用,則會異步執行。async 屬性僅適用於外部腳本(只有在使用 src 屬性時)。若是 async="async":腳本相對於頁面的其他部分異步地執行(當頁面繼續進行解析時,腳本將被執行)

script標籤的defer屬性

js文件在頁面解析到script標籤的時候開始下載,但並不會執行,dom加載完成執行。這兩個屬性的區別在於執行時機

動態腳本元素

js操做dom建立<script>標籤,自定義生成標籤的type、src屬性。文件會在該元素被添加到頁面的時候開始下載。ps:若是文件順序很重要的話,最好按照順序合成一個文件。而後再添加到頁面中。這樣:不管什麼時候啓動下載。文件的下載和執行過程不會阻塞頁面的其餘進程。

3. XHR ajax腳本注入、

用get請求一個文件,請求好了。而後建立動態腳本,最後添加進去。 缺陷:文件要再請求頁面的同一個域。不能從cdn下載

第一章加載和執行小結:

  1. 把文件放在body標籤簽名,
  2. 合併腳本,減小<script>標籤。
  3. 採用無阻塞下載js。使用script的defer和async屬性 異步下載。動態建立script標籤和執行代碼。

第二章-數據存取

  1. js四種基本的數據存取位置。 一、字面量:字符串、數字、布爾、對象、數組、函數、正則、null、undefined,字面量只表明自身,沒有存儲位置。 二、局部變量。 let var 聲明的變量。三、數組元素。四、對象成員。

性能:訪問字面量和局部變量的速度是最快的,訪問數組和對象成員相對較慢

  1. 變量標識符解析過程

搜索執行環境的做用域鏈,查找同名標識符。搜索過程從做用域鏈頭部開始,也就是當前運行函數的活動對象。若是找到,就使用這個標識符,對應的變量;若是沒有找到,繼續搜索下面的對象。搜索過程會持續進行,直到找到標識符,若沒法搜索到匹配的對象,那麼標識符被視爲未定義、

性能問題:一個標識符所在的位置越深,它的讀寫速度也就越慢。所以,函數中讀寫局部變量老是最快的,而讀寫全局變量一般是最慢的。

建議:將全局變量存儲到局部變量,加快讀寫速度。

  1. 閉包,原型,嵌套對象。

優化建議:將經常使用的跨做用域變量存儲到局部變量,而後直接訪問局部變量。理由如上,變量標識符解析過程。

第二章數據存取小結:

  1. 訪問字面量和局部變量的速度最快,相反,訪問數組元素和對象成員相對較慢。
  2. 因爲局部變量存在於做用域鏈的起始位置,由於訪問局部變量比訪問跨做用域變量更快。這個道理一樣適用於數組,對象,跨做用域變量
  3. 把經常使用的對象,數組,跨域變量保存在局部變量能夠改善js性能,局部變量訪問速度更快。

第三章DOM編程小結:

  1. dom操做天生就慢,儘可能減小dom操做,減小訪問dom的次數。
  2. 使用document.querySelect來作選擇器,比其餘方式快。
  3. 須要屢次訪問某個dom節點,使用局部變量存儲。
  4. html集合,把集合長度緩存到一個變量中,而後遍歷使用這個變量,若是常常操做集合,建議拷到一個數組中。
  5. 要留意瀏覽器的重繪和重排;批量修改樣式的時候,‘離線’操做DOM樹,使用緩存,並減小訪問佈局信息的次數。 重繪和重排是DOM編程優化的一個至關重要概念:重繪和重排
  6. 動畫中使用絕對定義,使用拖放處理。
  7. 使用事件委託來減小事件處理器的數量。

第四章算法和流程控制小結:

  1. for、while和do-while循環性能差很少,for-in循環速度只有前面幾種類型的1/7,因此儘可能避免使用for-in循環,除非你須要遍歷一個屬性數量未知的對象。

    forEach比for慢,若是運行速度要求嚴格,不建議使用。

  2. 改善循環性能的最佳方式是減小每次迭代的工做量和減小循環迭代的次數

減小迭代工做量:減小屬性查找和倒序循環,循環次數越多,性能優化越明顯。

for(var i=0;i<items.length;i++){代碼}//正序循環
    for(var i=items.length;i--){代碼}//倒序循環
    //減小屬性查找:查找一次屬性,把值存在局部變量,在控制條件裏面使用這個變量
    
    倒序循環在i>0的時候會自動轉換爲true,等於0的時候爲false。
    //倒序循環:控制條件從(迭代數少於總數嗎?它是否爲true?)變爲(它是否爲true)
複製代碼

減小迭代的次數:"Duff's Device"循環體展開技術,有興趣的能夠看一下,迭代工做量大於1000的時候適用。

  1. if-else與switch:條件數量越大,越傾向於使用switch。

    優化if-else:

    一、把最可能出現的條件放在首位。二、使用二分法把值域分紅一系列的區間。
    複製代碼
  2. 瀏覽器的調用棧大小限制了遞歸算法在js中的應用;棧溢出錯誤會致使其餘代碼中斷運行。

    當心使用遞歸,如今es6遞歸能夠尾遞歸,在es6中只要使用尾遞歸就不會發生棧溢出,相對節省性能。
    複製代碼

第五章字符串和正則表達式小結:

  1. 字符串合併的時候使用簡單的'+'和'+='操做符。

    str+='abc'+'efg;//2個以上的字符串拼接,會產生臨時字符串
    str=str+'abc'+'efg';//推薦,提速10%~40%  
    複製代碼
  2. 書裏面講的正則原理和回溯原理,這個很重要,找個篇博客:跟書裏面講的差很少,但仍是建議你們能夠去找找PDF好好看看正則表達式這節。

  3. 提升正則表達式效率的方法:

    一、最重要的是:具體化正則表達式!具體化正則表達式!具體化正則表達式!
     二、關注如何讓匹配更快失敗,找出一些必需,特殊的字符
     三、正則表達式以簡單的、必需的字元開始。
     四、使用量詞模式,使它們後面的字元互斥。
     五、較少分支數量,縮小分支範圍
     六、使用合適的量詞
     七、把正則表達式賦值給變量並重用
     八、將複雜的正則拆分爲簡單的片斷
     //事實上,書裏面講的方法不止這麼幾個,並且每個都有詳細的解析 大佬們 仍是去看看這一章節吧
    複製代碼
  4. 正則表達式並不老是最好的解決方案,當你只是搜索字面字符串或者你事先知道字符串的哪一部分將要被查找時:

    使用indexOf()和lastIndexOf()更適合查找特定字符串的位置或者判斷它們是否存在
     //例如:判斷當前瀏覽器之類。
    複製代碼

第六章快速響應的用戶界面小結:

js和用戶界面更新在同一個進程中運行,所以一次只能處理一件事情。高效的管理UI線程就是要確保js不能運行太長時間,以避免影響用戶體驗。

  1. 瀏覽器限制了js任務的運行時間,這種限制頗有必要,它確保某些惡意代碼不能經過永不中止的密集操做鎖住用戶的瀏覽器。此限制分爲兩種:調用棧的大小和長時間運行腳本。

  2. 任何js任務都不該當執行超過100毫秒。過長的運行時間會致使UI更新出現明顯延遲,從而對用戶體驗形成負面影響。

  3. 定時器可用來安排代碼延遲執行,它使得你能夠把長時間運行腳本分解成一系列的小任務。

第七章 AJAX 小結

這一章節貌似東西都比較老一點。。

  1. post更適合發送大量數據到服務器。

  2. get請求可以被瀏覽器緩存,Expires頭信息設置瀏覽器緩存請求多久。可用於從不改變的圖片或者其餘靜態數據集(js、css等)

  3. JSON是一種使用js對象和數組直接量編寫的輕量級且易於解析的數據格式,它不只傳輸尺寸小,並且解析速度快。JSON是高性能AJAX的基礎,尤爲在使用動態腳本注入時。

json應該是近幾年一直在用的。。。

  1. 減小請求數,經過合併js和css文件。
  2. 縮短頁面的加載時間,頁面主要內容加載完成後,用AJAX獲取那些次要的文件。

第八章編程實踐小結

  1. 避免雙重求值:避免使用eval()和 function()構造器來避免雙重求值帶來的性能消耗,一樣的,給setTimeout()和setInterval()傳遞函數而不是字符串做爲參數。

    //雙重求值:就是在js代碼中執行另外一段js代碼,不建議使用下面這些方式
     eval('代碼') 
     function構造函數--new function('代碼')
     setTimeout(‘代碼’,100)和setInterval(‘代碼’,100) 
    複製代碼
  2. 儘可能使用直接量建立對象和數組。直接量的建立和初始化都比非直接量形式要快。

  3. 避免作重複工做,能節省的步驟就節省。

  4. js原生方法總會比你寫的任何代碼都要快。

第九章 構建並部署高性能js應用小結

構建和部署的過程對基於js的web應用的性能有着巨大影響。這個過程當中最重要的步驟有:

  1. 合併、壓縮js文件。可以使用Gzip壓縮,可以減小約70%的體積

這些都是在構建過程當中完成的工做,不要等到運行時去作,webpack也是在構建過程當中,完成的工做。 2. 經過正確設置HTTP響應頭來緩存js文件,經過向文件名增長時間戳來避免緩存問題。 3. 使用CDN提供js文件;CDN不只能夠提高性能,它也爲你管理文件的壓縮與緩存,。

第十章 工具 小結:

當網頁變慢時,分析從網絡下載的資源以及分析的資源以及分析腳本的運行性能能讓你專一於那些最須要優化的地方。

  1. 使用網絡分析工具找出加載腳本和頁面中其餘資源的瓶頸,這會幫助你決定那些腳本須要延遲加載,或者須要進一步分析。

    檢查圖片、樣式表和腳本的加載過程,以及它們對頁面總體加載和渲染的影響。從而針對性的作出優化
    複製代碼
  2. 把腳本儘量延遲加載,這樣作能夠加快頁面渲染速度,給用戶帶來更好的體驗。

  3. 確認腳本和其餘資源文件的加載過程已經被優化

    這裏主要是說文件從服務器的下載速度,好比服務器那邊的配置問題之類的。
     栗子:我就被後端坑過。一個js文件就200k ,下載下來須要50秒鐘!
     後面發現原來是後端那邊nginx沒有開啓加速配置什麼的,致使出現的問題,找問題找半天。 
    複製代碼
  4. 測試腳本的運行時間,用一個Date實例減去另外一個Date實例,獲得的時間差就是腳本運行消耗的時間。

    let start=new Date();
     //你的代碼
     let time=newDate()-start;
    複製代碼
  5. chrome ,火狐 等主流瀏覽器的控制面板,已經可以反映不少性能問題。仔細分析就能找出不少問題。例如:資源加載,斷點等

後話

事實上,自認爲這本書最寶貴的就是一些提到的細節,好比:

一、字符串合併的時候使用簡單的'+'和'+='操做符。

str+='abc'+'efg;//2個以上的字符串拼接,會產生臨時字符串
    str=str+'abc'+'efg';//推薦,提速10%~40%
複製代碼

二、避免雙重求值:避免使用eval()和 function()構造器來避免雙重求值帶來的性能消耗,一樣的,給setTimeout()和setInterval()傳遞函數而不是字符串做爲參數。

//雙重求值:就是在js代碼中執行另外一段js代碼,不建議使用下面這些方式
    eval('代碼') 
    function構造函數--new function('代碼')
    setTimeout(‘代碼’,100)和setInterval(‘代碼’,100) 
複製代碼

這些東西可讓咱們知道什麼是更好的實踐,什麼樣的代碼能夠跑得更快,讓咱們養成更好的開發習慣。

書不太厚,若是對裏面的內容感興趣,仍是建議買一本回家看一看。

以上2018.1.9

相關文章
相關標籤/搜索