js遞迴遍歷樹結構_前端效能優化之遞迴
技術標籤:js遞迴遍歷樹結構
這次分享,我們主要來看看前端效能優化方面的內容。
前端效能優化,可以從很多方面進行,下面是效能優化的冰山一角的內容。
- JS、CSS壓縮合並(減少HTTP請求)
- 將CSS放在頭部、JavaScript檔案放在底部
- 第三方庫打包成vendor,單獨載入
- 資源按需載入(import(), require.ensure())
- 利用事件代理(事件冒泡)
- 減少DOM的重排與重繪(diff演算法)
- 動畫開啟GPU加速(will-change, transform)
- 圖片優化(圖片延遲載入,降低圖片質量等)
- 利用瀏覽器快取,強快取或者協商快取(webpack splitChunks,固定hash)
- 使用HTTP2(解析速度快、多路複用、頭部壓縮)
- 靜態資源使用CDN
- ....
效能優化是個值得研究和深入挖掘的一塊領域,不但可以在極大的提高使用者體驗,在這方面的技能提升也可以更上一層樓,成為“磚家”。
這裡抖下機靈。最近股市基金一片大好,賺錢效應明顯,坐在家裡吹空調(科技)、吃火鍋、喝小酒(消費)、看病(醫療)就可以賺到不少買菜錢。
單單從科技上來說噢,假如我們全面5G、全面新基建的情況下,5G相較於4G,資料傳輸速率遠遠高於4G,基本按照目前的情況來說,app、h5的訪問速度,理論上,都可以實現毫秒開。
按照這種情況來說,給各位前輩提一個抖機靈的問題,我們還需要搞這些什麼前端效能優化嗎..?
迴歸正題,迴歸正題。現階段我們還是需要優化的。但是在這次分享中,不講上述的優化哈,這裡主要說說程式碼層面遞迴的優化。
這次分享,主要從以下幾個點進行
- 遞迴wiki定義
- 遞迴帶來的問題
- 爆棧
- 迴圈引用
- 遞迴優化
- 尾呼叫優化
- 快取代理
- 遞迴轉遍歷
- 參考資料
遞迴wiki定義
遞迴(英語:Recursion),又譯為遞迴,在數學與電腦科學中,是指在函式的定義中使用函式自身的方法。通俗的點說呢,就是 方法自己呼叫自己。
遞迴思想就是:把問題分解成規模更小,但和原問題有著相同解法的問題。在實際開發過程中,遞迴有非常多的運用,比如深複製、斐波那契數列、階乘、求和等等。
使用遞迴,需要滿足一下條件,否則會導致爆棧。
- 自己呼叫自己
- 邊界條件
比如如下程式碼。用途不同,但是共性是一樣的。
// 階乘
function factorial (n: number): number {
if (n == 1) return n;
return n * factorial(n - 1)
}
console.log(factorial(5)) // 5 * 4 * 3 * 2 * 1 = 120
// 斐波那契數列
function fibonacci (n: number): (number | Function) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(5)) // 1 1 2 3 5
遞迴帶來的問題
遞迴帶來的問題,是災難性的。
首先,遞迴時沒有寫邊界條件。這種情況會觸發瀏覽器(Chrome)最大呼叫棧的錯誤RangeError: Maximum call stack size exceeded,導致badjs。也就是說上面所說的爆棧。Chrome(83.0.4103.116)支援最大呼叫棧為15673。如下測試程式碼。
let
其次,遞迴沒有爆棧,但阻塞js載入。這裡阻塞js載入並沒有觸發RangeError錯誤,但是由於呼叫次數過多,導致程式碼執行被拖慢。看看如下程式碼。
const
上面只是涉及到數學問題,我們回到業務場景看看。
最後,深拷貝迴圈引用導致爆棧。假設有一個cloneTest函式,接受一個物件作為引數,返回一個物件。如下程式碼。
function
遞迴優化
以上簡單的說了下函式遞迴引發的一些問題。如如遞迴爆棧、阻塞js執行等,進而引起badjs激增,嚴重影響使用者體驗。
針對種種情況,我們是有辦法來解決的。
尾呼叫優化
尾呼叫,是指函式內部的最後一個動作是函式呼叫。該呼叫的返回值,直接返回給函式。尾呼叫不需要儲存當前詞法作用域(外層函式的呼叫棧內容)資訊,直接用內層函式的呼叫棧,取代外層函式的呼叫棧。
// 尾呼叫
上面程式碼中,情況一是呼叫函式g之後,還有賦值操作,所以不屬於尾呼叫,即使語義完全一樣。情況二也屬於呼叫後還有操作,即使寫在一行內。情況三等同於下面的程式碼。
function
尾呼叫和非尾呼叫的差別在於,它們的函式上下文不同。
在實際開發過程中,尾呼叫的實現,可以將詞法作用域用到的引數,都放到函式引數中。 如下程式碼。
// 純遞迴
效率極大極大的提升。
快取代理
還是以斐波那契數列舉 。如果每次遞迴都依賴上一次的結果,那麼就可以利用代理模式中的快取代理,來實現效率上的提升。
// 快取代理
遞迴轉遍歷
遞迴轉遍歷,可以避免呼叫棧過多導致爆棧的問題,方法的實現主要是利用了棧結構。
function
以上。
參考文章
- 函式的擴充套件
- 深拷貝的終極探索(99%的人都不知道)