寫於 2017.02.16javascript
入手《高性能JavaScript》一週後,終於斷斷續續看完了。簡要說說感覺,就是這本書很是薄,很是容易看,認真看的話其實兩三個小時就能翻一遍了。這篇文章也是做爲一篇閱讀筆記,用來記錄我在閱讀過程當中的一些理解與感悟。html
乍一看上去,這本書裏面有至關一部份內容是很是舊的,不少優化手段在現在高速的網絡環境以及先進的瀏覽器的加持下,視乎已經失去了必要性。然而做爲一個有潔癖的人,是沒法容許本身的代碼「大概差很少就好」,並且我也相信任何一個有追求的人都但願本身的做品是精益求精的,因此這本書仍然有很是大的學習意義。拋開主觀,在閱讀一些優秀開源庫時,看到別人的某些代碼很是不理解,在看完這本書之後回想起來才發出感慨,原來人家這麼作的目的是爲了提高性能。前端
全書共分爲十章內容,如下將按照書本章節的順序,來逐一撰寫個人閱讀筆記。html5
把js放在</body>
結束標籤以前而不是<head></head>
標籤內部可以避免瀏覽器阻塞,提高用戶體驗,已經算是一個常識。這個常識的背後,涉及到了瀏覽器單進程的概念。java
事實上,多數瀏覽器使用單一進程來處理用戶界面(UI)刷新和javascript腳本執行,因此同一時刻只能作一件事。web
這裏說的用戶界面刷新,指的是咱們「所能看到的UI」變化(好比點擊一個按鈕,會出現按鈕被按下去的效果)。換句話來講,處理UI就沒法處理javascript,反之亦然。因此若是一份運行時間很長的js腳本放在頁面頂端,會阻塞以後頁面的下載和渲染,給用戶的感受就是「頁面一片空白卡死不會動」。ajax
雖然如今網速和瀏覽器的效率已經獲得了巨大的提高,但隨着移動端的興起以及前端框架如Vue
,React
的大量使用,這個問題仍是很是值得咱們注意的。正則表達式
首先對於數據的存取,有如下這麼一句關鍵:算法
每個js函數都會帶有一個叫作
[[Scope]]
的內部屬性,也就是該函數的做用域鏈,它決定了哪些數據能被函數訪問。編程
書上詳細介紹了做用域鏈
、執行上下文
、活動對象
、全局對象
、閉包
等概念,在這裏就不進行復述了。用我本身理解的話來講,就是一個函數若要使用一個變量,它會從最近的地方,也就是定義在函數內部的局部變量裏面去找;若沒有找到,則往更遠處的全局變量(或者上一級做用域)裏面去找。偏偏是這個「找」的過程,產生了性能的問題。書上使用了「解析標識符」來表述「找」這個動做,而js性能偏偏是隨着解析標識符深度的增長而下降,因此在最佳實踐裏,每每是經過把一個較深的變量賦值給一個局部變量,在函數內部直接調用這個局部變量來提高性能。
說完變量,就到了方法。在js中一切皆對象,然而js的對象是基於原型而來,這就引出了一個原型鏈的概念。與前文關於解析標識符的原理相似,要調用一個對象中的方法,首先會從這個對象實例中查找,若找不到,則會沿着其原型鏈一步一步由近到遠地往上查找,其性能也是隨之降低的。
另外,書上也討論到了關於「嵌套成員」的問題。好比window.location.href
,它會先找到window
對象,而後查找嵌套於內的location
對象,再找到這裏面的href
屬性,前先後後套了多層,在性能上也有着必定的花銷。因此在實際的編碼過程當中,咱們更多時候會面對的每每是這種嵌套成員的問題,時刻記得緩存對象成員的值,在執行完畢後利用cacheObj = null
的方式釋放緩存,能夠有效地提升性能,以下例子:
// bad
document.querySelector('.xxx').style.margin = 10 + 'px'
document.querySelector('.xxx').style.padding = 10 + 'px'
document.querySelector('.xxx').style.color = 'pink'
// good
let xxxStyle = document.querySelector('.xxx').style
xxxStyle.margin = 10 + 'px'
xxxStyle.padding = 10 + 'px'
xxxStyle.color = 'pink'
xxxStyle = null
複製代碼
這一章節詳細介紹了關於dom操做的一系列問題。首先要明確一個知識點就是dom操做是具備「天生就慢」的問題。爲何會如此呢?由於在瀏覽器裏面,處理html和js是兩套不一樣的機制,他們經過接口來進行聯繫的。引用書中的原話,就是能夠把html和js理解爲兩座島,他們之間須要一座橋來進行溝通,而過橋則會產生時間與成本上面的開銷,也所以引發了性能的問題。這一章節經過分析不一樣的dom操做函數,來綜合對比了各類方法的速度。
dom操做每每容易引發瀏覽器的重繪與重排。重排,指的是頁面的佈局和幾何屬性改變時所發生的事情;重繪,是指把dom元素繪製到屏幕上面的過程。
會形成性能問題的,每每來自於重排,由於瀏覽器須要從新計算頁面全部元素的大小與位置,而後把它們安置在正確的地方。因此,要提高頁面的性能,很重要的一個舉措就是避免頁面的重排。
值得注意的是,並不是只有在修改頁面元素的大小和位置的時候纔會引起重排,在獲取的時候瀏覽器也會出發重排,以返回正確的值。
然而不少時候咱們不得不直接操做dom,儘管它們會引發重排和重繪。書上給出了幾個方案,都能有效提高性能。其實方法和上文關於js緩存局部變量的方法相似,也是經過緩存的機制,減小對於dom元素屬性的查找,以及批量修改變量再一次性更新dom的辦法去減小查詢與修改。
除此之外,讓元素脫離文檔流也是一個很好的方法。由於元素一旦脫離文檔流,它對其餘元素的影響幾乎爲零,性能的損耗就可以有效侷限於一個較小的範圍。
講完重排與重繪,往dom元素上綁定事件也是引發性能問題的元兇。利用瀏覽器自帶的冒泡或捕獲機制,能夠經過事件委託的方式減小事件處理器的數量,從而把性能優化得更好。
這一章首先分析了幾種循環類型,結論是隻有for-in
循環的性能最慢,由於每次迭代都會同事搜索實例或原型屬性,致使其性能只有其它類型速度的1/7。 循環在代碼中很是常見,既然沒法避免,則須要經過儘可能減小循環次數,減輕每次循環的工做量的方式提高性能。
對於條件語句if else
或者switch
,其性能在現實中並無太大區別,關鍵是要正確處理語義化的需求。有的時候也可使用查表法進行。
對於遞歸算法,最好的提高性能方法是緩存上次執行的結果,在下一次遞歸的時候直接引用而非從頭開始計算。
TODO…… (對於正則表達式尚未特別熟悉,這一章跳着看了)
前面五章都是針對JS原生的語法分析性能問題,從這一章開始分析針對用戶界面的可感知性能問題。
因爲瀏覽器是單線程運做的,在處理UI事件的時候沒法處理js事件,反之亦然,因此對於耗時過長的js任務來講,可使用定時器的方法使其讓出線程控制權,讓瀏覽器優先處理UI事件以提高用戶體驗。
html5新增的web worker
容許多開線程,意味着耗時較長、性能損耗較大的js任務能夠放到web worker
中進行,而無需阻塞瀏覽器UI線程的執行。值得注意的是,web worker
沒法使用瀏覽器相關的資源,因此沒法用以進行dom操做等。
ajax
技術已是現在的主流技術,在這裏就無需贅述了。書上關於其性能優化的內容,多集中在瀏覽器資源緩存上。若是可以有效利用瀏覽器的緩存機制,能夠大大減小與服務端的交互,提高性能。
書上沒有說起的是如今逐漸開始流行的fetch API
,關於這方面的性能的問題也值得咱們研究。
剩下的內容都是一些編程實踐,代碼優化等等。
在現在的前端開發領域中,上線的代碼通常都會通過代碼合併、壓縮,服務端開啓gzip等工做。隨着http2的發展,網頁的性能更會獲得提升,可能傳統」文件合併「這一工做會逐漸被摒棄。另外http2的服務端推送也能極大地提高頁面加載速度,這部份內容在我另一篇文章《深刻研究:HTTP2 的真正性能到底如何》有詳細研究,有興趣的讀者能夠去看看。
《高性能JavaScript》這本書很是精緻,內容也很是豐富。這篇讀書筆記僅僅做爲首次閱讀草草而做的讀書筆記,對於書中內容的理解或多或少都會有失偏頗,若是發現有錯漏或者更好的理解方式,歡迎留言和我交流~