八月面試題(2) 2018-9-6

楊季布發表於2018-09-06

1、 什麼是重繪(Repaint)和迴流(Reflow)?

哪些動作可能會導致重繪(Repaint)和迴流(Reflow)的發生?

重繪(Repaint)和迴流(Reflow)和Event loop的關係?

如何減少重繪(Repaint)和迴流(Reflow)?

答: 重繪和迴流是渲染步驟中的一小節,但是這兩個步驟對於效能影響很大。 1. 重繪是當節點需要更改外觀而不會影響佈局的,比如改變 color 就叫稱為重繪 2. 迴流是佈局或者幾何屬性需要改變就稱為迴流。

迴流必定會發生重繪,重繪不一定會引發迴流。迴流所需的成本比重繪高的多,改變深層次的節點很可能導致父節點的一系列迴流。 所以以下幾個動作可能會導致效能問題:

  • 改變 window 大小
  • 改變字型
  • 新增或刪除樣式
  • 文字改變
  • 定位或者浮動
  • 盒模型

很多人不知道的是,重繪和迴流其實和 Event loop 有關。

  • 當 Event loop 執行完 Microtasks 後,會判斷 document 是否需要更新。因為瀏覽器是 60Hz 的重新整理率,每 16ms 才會更新一次。
  • 然後判斷是否有 resize 或者 scroll ,有的話會去觸發事件,所以 resize 和 scroll 事件也是至少 16ms 才會觸發一次,並且自帶節流功能。
  • 判斷是否觸發了 media query
  • 更新動畫並且傳送事件
  • 判斷是否有全屏操作事件
  • 執行 requestAnimationFrame 回撥
  • 執行 IntersectionObserver 回撥,該方法用於判斷元素是否可見,可以用於懶載入上,但是相容性不好
  • 更新介面
  • 以上就是一幀中可能會做的事情。如果在一幀中有空閒時間,就會去執行 requestIdleCallback 回撥。

少重繪和迴流 使用 translate 替代 top

  • 使用 visibility 替換 display: none ,因為前者只會引起重繪,後者會引發迴流(改變了佈局)
  • 把 DOM 離線後修改,比如:先把 DOM 給 display:none (有一次 Reflow),然後你修改100次,然後再把它顯示出來
  • 不要把 DOM 結點的屬性值放在一個迴圈裡當成迴圈裡的變數

for(let i = 0; i < 1000; i++) { // 獲取 offsetTop 會導致迴流,因為需要去獲取正確的值 console.log(document.querySelector('.test').style.offsetTop) }

不要使用 table 佈局,可能很小的一個小改動會造成整個 table 的重新佈局

  • 動畫實現的速度的選擇,動畫速度越快,迴流次數越多,也可以選擇使用 requestAnimationFrame
  • CSS 選擇符從右往左匹配查詢,避免 DOM 深度過深
  • 將頻繁執行的動畫變為圖層,圖層能夠阻止該節點回流影響別的元素。比如對於 video 標籤,瀏覽器會自動將該節點變為圖層。

2、 https抓包的原理是什麼?平時你用什麼工具?如何抓包?

3、什麼是無頭瀏覽器?它的作用是什麼?

4、請說一下V8下的垃圾回收機制

答:V8 實現了準確式 GC,GC 演算法採用了分代式垃圾回收機制。因此,V8 將記憶體(堆)分為新生代和老生代兩部分。 新生代演算法 新生代中的物件一般存活時間較短,使用 Scavenge GC 演算法。 在新生代空間中,記憶體空間分為兩部分,分別為 From 空間和 To 空間。在這兩個空間中,必定有一個空間是使用的,另一個空間是空閒的。新分配的物件會被放入 From 空間中,當 From 空間被佔滿時,新生代 GC 就會啟動了。演算法會檢查 From 空間中存活的物件並複製到 To 空間中,如果有失活的物件就會銷燬。當複製完成後將 From 空間和 To 空間互換,這樣 GC 就結束了。 #老生代演算法

老生代演算法 老生代中的物件一般存活時間較長且數量也多,使用了兩個演算法,分別是標記清除演算法和標記壓縮演算法。 在講演算法前,先來說下什麼情況下物件會出現在老生代空間中: 新生代中的物件是否已經經歷過一次 Scavenge 演算法,如果經歷過的話,會將物件從新生代空間移到老生代空間中。 To 空間的物件佔比大小超過 25 %。在這種情況下,為了不影響到記憶體分配,會將物件從新生代空間移到老生代空間中。 老生代中的空間很複雜,有如下幾個空間

    enum AllocationSpace {
複製程式碼

// TODO(v8:7464): Actually map this space's memory as read-only. RO_SPACE, // 不變的物件空間 NEW_SPACE, // 新生代用於 GC 複製演算法的空間 OLD_SPACE, // 老生代常駐物件空間 CODE_SPACE, // 老生代程式碼物件空間 MAP_SPACE, // 老生代 map 物件 LO_SPACE, // 老生代大空間物件 NEW_LO_SPACE, // 新生代大空間物件

FIRST_SPACE = RO_SPACE, LAST_SPACE = NEW_LO_SPACE, FIRST_GROWABLE_PAGED_SPACE = OLD_SPACE, LAST_GROWABLE_PAGED_SPACE = MAP_SPACE }; 在老生代中,以下情況會先啟動標記清除演算法:

  • 某一個空間沒有分塊的時候
  • 空間中被物件超過一定限制
  • 空間不能保證新生代中的物件移動到老生代中

在這個階段中,會遍歷堆中所有的物件,然後標記活的物件,在標記完成後,銷燬所有沒有被標記的物件。在標記大型對記憶體時,可能需要幾百毫秒才能完成一次標記。這就會導致一些效能上的問題。為了解決這個問題,2011 年,V8 從 stop-the-world 標記切換到增量標誌。在增量標記期間,GC 將標記工作分解為更小的模組,可以讓 JS 應用邏輯在模組間隙執行一會,從而不至於讓應用出現停頓情況。但在 2018 年,GC 技術又有了一個重大突破,這項技術名為併發標記。該技術可以讓 GC 掃描和標記物件時,同時允許 JS 執行,你可以點選 該部落格 詳細閱讀。

清除物件後會造成堆記憶體出現碎片的情況,當碎片超過一定限制後會啟動壓縮演算法。在壓縮過程中,將活的物件像一端移動,直到所有物件都移動完成然後清理掉不需要的記憶體。

5、線性順序儲存結構和鏈式儲存結構有什麼區別?以及優缺點。

答: 順序儲存結構和鏈式儲存結構的區別 連結串列儲存結構的記憶體地址不一定是連續的,但順序儲存結構的記憶體地址一定是連續的; 鏈式儲存適用於在較頻繁地插入、刪除、更新元素時,而順序儲存結構適用於頻繁查詢時使用。

順序儲存結構和鏈式儲存結構的優缺點: 空間上 順序比鏈式節約空間。是因為鏈式結構每一個節點都有一個指標儲存域。 儲存操作上: 順序支援隨機存取,方便操作 插入和刪除上: 鏈式的要比順序的方便(因為插入的話順序表也很方便,問題是順序表的插入要執行更大的空間複雜度,包括一個從表頭索引以及索引後的元素後移,而連結串列是索引後,插入就完成了) 例如:當你在字典中查詢一個字母j的時候,你可以選擇兩種方式,第一,順序查詢,從第一頁依次查詢直到查詢到j。第二,索引查詢,從字典的索引中,直接查出j的頁數,直接找頁數,或許是比順序查詢最快的。

相關文章