摘要:除此以外,讓元素脫離文檔流也是一個很好的方法。因為元素一旦脫離文檔流,它對其他元素的影響幾乎為零,性能的損耗就能夠有效局限于一個較小的范圍。講完重排與重繪,往元素上綁定事件也是引起性能問題的元兇。高性能這本書非常精致,內(nèi)容也非常豐富。
入手《高性能JavaScript》一周后,終于斷斷續(xù)續(xù)看完了。簡要說說感受,就是這本書非常薄,非常容易看,認真看的話其實兩三個小時就能翻一遍了。這篇文章也是作為一篇閱讀筆記,用來記錄我在閱讀過程中的一些理解與感悟。
乍一看上去,這本書里面有相當一部分內(nèi)容是非常舊的,很多優(yōu)化手段在如今高速的網(wǎng)絡(luò)環(huán)境以及先進的瀏覽器的加持下,視乎已經(jīng)失去了必要性。然而作為一個有潔癖的人,是無法允許自己的代碼“大概差不多就好”,而且我也相信任何一個有追求的人都希望自己的作品是精益求精的,所以這本書仍然有非常大的學(xué)習(xí)意義。拋開主觀,在閱讀一些優(yōu)秀開源庫時,看到別人的某些代碼非常不理解,在看完這本書以后回想起來才發(fā)出感慨,原來人家這么做的目的是為了提升性能。
全書共分為十章內(nèi)容,以下將按照書本章節(jié)的順序,來逐一撰寫我的閱讀筆記。
一、加載和執(zhí)行把js放在
結(jié)束標簽之前而不是標簽內(nèi)部能夠避免瀏覽器阻塞,提升用戶體驗,已經(jīng)算是一個常識。這個常識的背后,涉及到了瀏覽器單進程的概念。
事實上,多數(shù)瀏覽器使用單一進程來處理用戶界面(UI)刷新和javascript腳本執(zhí)行,所以同一時刻只能做一件事。
這里說的用戶界面刷新,指的是我們“所能看到的UI”變化(比如點擊一個按鈕,會出現(xiàn)按鈕被按下去的效果)。換句話來說,處理UI就無法處理javascript,反之亦然。所以如果一份運行時間很長的js腳本放在頁面頂端,會阻塞之后頁面的下載和渲染,給用戶的感覺就是“頁面一片空白卡死不會動”。
雖然現(xiàn)在網(wǎng)速和瀏覽器的效率已經(jīng)得到了巨大的提升,但隨著移動端的興起以及前端框架如Vue,React的大量使用,這個問題還是非常值得我們注意的。
二、數(shù)據(jù)存取首先對于數(shù)據(jù)的存取,有以下這么一句關(guān)鍵:
每一個js函數(shù)都會帶有一個叫做[[Scope]]的內(nèi)部屬性,也就是該函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問。
書上詳細介紹了作用域鏈、執(zhí)行上下文、活動對象、全局對象、閉包等概念,在這里就不進行復(fù)述了。用我自己理解的話來說,就是一個函數(shù)若要使用一個變量,它會從最近的地方,也就是定義在函數(shù)內(nèi)部的局部變量里面去找;若沒有找到,則往更遠處的全局變量(或者上一級作用域)里面去找。恰恰是這個“找”的過程,產(chǎn)生了性能的問題。書上使用了“解析標識符”來表述“找”這個動作,而js性能恰恰是隨著解析標識符深度的增加而降低,所以在最佳實踐里,往往是通過把一個較深的變量賦值給一個局部變量,在函數(shù)內(nèi)部直接調(diào)用這個局部變量來提升性能。
說完變量,就到了方法。在js中一切皆對象,然而js的對象是基于原型而來,這就引出了一個原型鏈的概念。與前文關(guān)于解析標識符的原理類似,要調(diào)用一個對象中的方法,首先會從這個對象實例中查找,若找不到,則會沿著其原型鏈一步一步由近到遠地往上查找,其性能也是隨之下降的。
另外,書上也討論到了關(guān)于“嵌套成員”的問題。比如window.location.href,它會先找到window對象,然后查找嵌套于內(nèi)的location對象,再找到這里面的href屬性,前前后后套了多層,在性能上也有著一定的花銷。所以在實際的編碼過程中,我們更多時候會面對的往往是這種嵌套成員的問題,時刻記得緩存對象成員的值,在執(zhí)行完畢后利用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
這一章節(jié)詳細介紹了關(guān)于dom操作的一系列問題。首先要明確一個知識點就是dom操作是具有“天生就慢”的問題。為什么會如此呢?因為在瀏覽器里面,處理html和js是兩套不同的機制,他們通過接口來進行聯(lián)系的。引用書中的原話,就是可以把html和js理解為兩座島,他們之間需要一座橋來進行溝通,而過橋則會產(chǎn)生時間與成本上面的開銷,也因此引起了性能的問題。這一章節(jié)通過分析不同的dom操作函數(shù),來綜合對比了各種方法的速度。
dom操作往往容易引起瀏覽器的重繪與重排。重排,指的是頁面的布局和幾何屬性改變時所發(fā)生的事情;重繪,是指把dom元素繪制到屏幕上面的過程。
會造成性能問題的,往往來自于重排,因為瀏覽器需要重新計算頁面所有元素的大小與位置,然后把它們安置在正確的地方。所以,要提升頁面的性能,很重要的一個舉措就是避免頁面的重排。
值得注意的是,并非只有在修改頁面元素的大小和位置的時候才會引發(fā)重排,在獲取的時候瀏覽器也會出發(fā)重排,以返回正確的值。
然而很多時候我們不得不直接操作dom,盡管它們會引起重排和重繪。書上給出了幾個方案,都能有效提升性能。其實方法和上文關(guān)于js緩存局部變量的方法類似,也是通過緩存的機制,減少對于dom元素屬性的查找,以及批量修改變量再一次性更新dom的辦法去減少查詢與修改。
除此以外,讓元素脫離文檔流也是一個很好的方法。因為元素一旦脫離文檔流,它對其他元素的影響幾乎為零,性能的損耗就能夠有效局限于一個較小的范圍。
講完重排與重繪,往dom元素上綁定事件也是引起性能問題的元兇。利用瀏覽器自帶的冒泡或捕獲機制,可以通過事件委托的方式減少事件處理器的數(shù)量,從而把性能優(yōu)化得更好。
四、算法和流程控制這一章首先分析了幾種循環(huán)類型,結(jié)論是只有for-in循環(huán)的性能最慢,因為每次迭代都會同事搜索實例或原型屬性,導(dǎo)致其性能只有其它類型速度的1/7。
循環(huán)在代碼中非常常見,既然無法避免,則需要通過盡量減少循環(huán)次數(shù),減輕每次循環(huán)的工作量的方式提升性能。
對于條件語句if else或者switch,其性能在現(xiàn)實中并沒有太大區(qū)別,關(guān)鍵是要正確處理語義化的需求。有的時候也可以使用查表法進行。
對于遞歸算法,最好的提升性能方法是緩存上次執(zhí)行的結(jié)果,在下一次遞歸的時候直接引用而非從頭開始計算。
五、字符串和正則表達式TODO……
(對于正則表達式還沒有特別熟悉,這一章跳著看了)
前面五章都是針對JS原生的語法分析性能問題,從這一章開始分析針對用戶界面的可感知性能問題。
由于瀏覽器是單線程運作的,在處理UI事件的時候無法處理js事件,反之亦然,所以對于耗時過長的js任務(wù)來說,可以使用定時器的方法使其讓出線程控制權(quán),讓瀏覽器優(yōu)先處理UI事件以提升用戶體驗。
html5新增的web worker允許多開線程,意味著耗時較長、性能損耗較大的js任務(wù)可以放到web worker中進行,而無需阻塞瀏覽器UI線程的執(zhí)行。值得注意的是,web worker無法使用瀏覽器相關(guān)的資源,所以無法用以進行dom操作等。
七、Ajaxajax技術(shù)已經(jīng)是如今的主流技術(shù),在這里就無需贅述了。書上關(guān)于其性能優(yōu)化的內(nèi)容,多集中在瀏覽器資源緩存上。如果能夠有效利用瀏覽器的緩存機制,可以大大減少與服務(wù)端的交互,提升性能。
書上沒有提及的是現(xiàn)在逐漸開始流行的fetch API,關(guān)于這方面的性能的問題也值得我們研究。
其他剩下的內(nèi)容都是一些編程實踐,代碼優(yōu)化等等。
在如今的前端開發(fā)領(lǐng)域中,上線的代碼一般都會經(jīng)過代碼合并、壓縮,服務(wù)端開啟gzip等工作。隨著http2的發(fā)展,網(wǎng)頁的性能更會得到提高,可能傳統(tǒng)”文件合并“這一工作會逐漸被摒棄。另外http2的服務(wù)端推送也能極大地提升頁面加載速度,這部分內(nèi)容在我另外一篇文章《深入研究:HTTP2 的真正性能到底如何》有詳細研究,有興趣的讀者可以去看看。
《高性能JavaScript》這本書非常精致,內(nèi)容也非常豐富。這篇讀書筆記僅僅作為首次閱讀草草而作的讀書筆記,對于書中內(nèi)容的理解或多或少都會有失偏頗,如果發(fā)現(xiàn)有錯漏或者更好的理解方式,歡迎留言和我交流~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88133.html
摘要:局部變量位于作用域鏈的起始位置,因此訪問速度最快全局變量位于作用域鏈的最末端,因此訪問速度最慢。如訪問時間實例屬性第一層原型屬性第二層原型屬性在同一個函數(shù)中沒必要多次讀取同一個對象成員,建議第一次查詢到值后就將其存儲在局部變量中。 javascript中有四種基本的數(shù)據(jù)存取位置:字面量、變量、數(shù)組元素、對象成員。 1.訪問字面量和局部變量的速度最快,訪問數(shù)組元素和對象成員相對較慢。 2...
摘要:設(shè)計模式與開發(fā)實踐讀書筆記最近利用碎片時間在上面閱讀設(shè)計模式與開發(fā)實踐讀書這本書,剛開始閱讀前兩章內(nèi)容,和大家分享下我覺得可以在項目中用的上的一些筆記。事件綁定暫時這么多,以后會不定期更新一些關(guān)于我讀這本書的筆記內(nèi)容 JavaScript 設(shè)計模式與開發(fā)實踐讀書筆記 最近利用碎片時間在 Kindle 上面閱讀《JavaScript 設(shè)計模式與開發(fā)實踐讀書》這本書,剛開始閱讀前兩章內(nèi)容,...
摘要:作用域鏈查找作用域鏈的查找是逐層向上查找。而全局變量和閉包則會與之相反,繼續(xù)保存,所以使用用后需手動標記清除,以免造成內(nèi)存泄漏。獲取元素的屬性獲取元素的屬性等參考文檔高級程序設(shè)計作者以樂之名本文原創(chuàng),有不當?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVburXV?w=500&h=399); 作用域鏈查找 作用域鏈的查找是逐層向上查找。查...
摘要:包括元素的高度上下內(nèi)邊距上下邊框值,如果元素的的值為那么該值為。該值為元素的包含元素。最后,所有這些偏移量都是只讀的,而且每次訪問他們都需要重新計算。為了避免重復(fù)計算,可以將計算的值保存起來,以提高性能。 offsetHeight 包括元素的高度、上下內(nèi)邊距、上下邊框值,如果元素的style.display的值為none,那么該值為0。offsetWidth 包括元素的寬度、左...
摘要:設(shè)定瀏覽器屬性的屬性的方法叫做駝峰式命名是函數(shù)名方法名和對象屬性名的命名首選格式。由瀏覽器預(yù)先定義的對象被稱為宿主對象。在給某個元素添加了事件處理函數(shù)后,一旦事件發(fā)生,相應(yīng)的代碼就會執(zhí)行。 1.JavaScript是一個使網(wǎng)頁具有交互能力的程序設(shè)計語言。 2.設(shè)定瀏覽器屬性的屬性的方法叫做BOM. 3.駝峰式命名(myMood)是函數(shù)名、方法名和對象屬性名的命名首選格式。 4.命名變量...
閱讀 3326·2021-11-19 11:36
閱讀 2927·2021-09-27 13:34
閱讀 1990·2021-09-22 15:17
閱讀 2404·2019-08-30 13:49
閱讀 754·2019-08-26 13:58
閱讀 1359·2019-08-26 10:47
閱讀 2538·2019-08-23 18:05
閱讀 600·2019-08-23 14:25