【半月刊 2】前端高頻面試題及答案彙總

更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看javascript

------ 如下是正文 ------css

引言

半月刊第二期來啦,這段時間 Daily-Interview-Question 新增了 10 道高頻面試題,今天就把最近半月彙總的面試題和部分答案發給你們,幫助你們查漏補缺。前端

歡迎 PR 你認爲不錯的面試題,歡迎在項目 Issue 區留下你的答案,若有問題歡迎討論。java

項目地址是:Daily-Interview-Questionnode

第 15 題:簡單講解一下 HTTP2 的多路複用

在 HTTP/1 中,每次請求都會創建一次TCP鏈接,也就是咱們常說的3次握手4次揮手,這在一次請求過程當中佔用了至關長的時間,即便開啓了 Keep-Alive ,解決了屢次鏈接的問題,可是依然有兩個效率上的問題:react

  • 第一個:串行的文件傳輸。當請求a文件時,b文件只能等待,等待a鏈接到服務器、服務器處理文件、服務器返回文件,這三個步驟。咱們假設這三步用時都是1秒,那麼a文件用時爲3秒,b文件傳輸完成用時爲6秒,依此類推。(注:此項計算有一個前提條件,就是瀏覽器和服務器是單通道傳輸)
  • 第二個:鏈接數過多。咱們假設Apache設置了最大併發數爲300,由於瀏覽器限制,瀏覽器發起的最大請求數爲6(Chrome),也就是服務器能承載的最高併發爲50,當第51我的訪問時,就須要等待前面某個請求處理完成。

HTTP2採用二進制格式傳輸,取代了HTTP1.x的文本格式,二進制格式解析更高效。 多路複用代替了HTTP1.x的序列和阻塞機制,全部的相同域名請求都經過同一個TCP鏈接併發完成。在HTTP1.x中,併發多個請求須要多個TCP鏈接,瀏覽器爲了控制資源會有6-8個TCP鏈接都限制。 HTTP2中css3

  • 同域名下全部通訊都在單個鏈接上完成,消除了因多個 TCP 鏈接而帶來的延時和內存消耗。
  • 單個鏈接上能夠並行交錯的請求和響應,之間互不干擾

更多解析:github.com/Advanced-Fr…git

第 16 題:談談你對 TCP 三次握手和四次揮手的理解

image

更多解析:github.com/Advanced-Fr…github

第 17 題:A、B 機器正常鏈接後,B 機器忽然重啓,問 A 此時處於 TCP 什麼狀態

若是A 與 B 創建了正常鏈接後,從未相互發過數據,這個時候 B 忽然機器重啓,問 A 此時處於 TCP 什麼狀態?如何消除服務器程序中的這個狀態?(超綱題,瞭解便可)面試

由於B會在重啓以後進入tcp狀態機的listen狀態,只要當a從新發送一個數據包(不管是syn包或者是應用數據),b端應該會主動發送一個帶rst位的重置包來進行鏈接重置,因此a應該在syn_sent狀態。

image-20190303200744237

image-20190303211913869

更多解析:github.com/Advanced-Fr…

第 18 題:React 中 setState 何時是同步的,何時是異步的?

在 React 中,若是是由 React 引起的事件處理(好比經過 onClick 引起的事件處理),調用 setState 不會同步更新 this.state,除此以外的 setState 調用會同步執行 this.state。所謂「除此以外」,指的是繞過 React 經過 addEventListener 直接添加的事件處理函數,還有經過 setTimeout/setInterval 產生的異步調用。

**緣由:**在 React 的 setState 函數實現中,會根據一個變量 isBatchingUpdates 判斷是直接更新 this.state 仍是放到隊列中回頭再說,而 isBatchingUpdates 默認是 false,也就表示 setState 會同步更新 this.state,可是,有一個函數 batchedUpdates,這個函數會把 isBatchingUpdates 修改成t rue,而當 React 在調用事件處理函數以前就會調用這個 batchedUpdates,形成的後果就是由 React 控制的事件處理過程 setState 不會同步更新 this.state。

更多解析:github.com/Advanced-Fr…

第 19 題:React setState 筆試題,下面的代碼輸出什麼?

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 4 次 log
    }, 0);
  }

  render() {
    return null;
  }
};
複製代碼

解析:

一、第一次和第二次都是在 react 自身生命週期內,觸發時 isBatchingUpdates 爲 true,因此並不會直接執行更新 state,而是加入了 dirtyComponents,因此打印時獲取的都是更新前的狀態 0。

二、兩次 setState 時,獲取到 this.state.val 都是 0,因此執行時都是將 0 設置成 1,在 react 內部會被合併掉,只執行一次。設置完成後 state.val 值爲 1。

三、setTimeout 中的代碼,觸發時 isBatchingUpdates 爲 false,因此可以直接進行更新,因此連着輸出 2,3。

輸出: 0 0 2 3

更多解析:github.com/Advanced-Fr…

第 20 題:介紹下 npm 模塊安裝機制,爲何輸入 npm install 就能夠自動安裝對應的模塊?

解析:

1. npm 模塊安裝機制:

  • 發出npm install命令
  • 查詢node_modules目錄之中是否已經存在指定模塊
    • 若存在,再也不從新安裝
    • 若不存在
      • npm 向 registry 查詢模塊壓縮包的網址
      • 下載壓縮包,存放在根目錄下的.npm目錄裏
      • 解壓壓縮包到當前項目的node_modules目錄

2. npm 實現原理

輸入 npm install 命令並敲下回車後,會經歷以下幾個階段(以 npm 5.5.1 爲例):

  1. 執行工程自身 preinstall

當前 npm 工程若是定義了 preinstall 鉤子此時會被執行。

  1. 肯定首層依賴模塊

首先須要作的是肯定工程中的首層依賴,也就是 dependencies 和 devDependencies 屬性中直接指定的模塊(假設此時沒有添加 npm install 參數)。

工程自己是整棵依賴樹的根節點,每一個首層依賴模塊都是根節點下面的一棵子樹,npm 會開啓多進程從每一個首層依賴模塊開始逐步尋找更深層級的節點。

  1. 獲取模塊

獲取模塊是一個遞歸的過程,分爲如下幾步:

  • 獲取模塊信息。在下載一個模塊以前,首先要肯定其版本,這是由於 package.json 中每每是 semantic version(semver,語義化版本)。此時若是版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有該模塊信息直接拿便可,若是沒有則從倉庫獲取。如 packaeg.json 中某個包的版本是 ^1.1.0,npm 就會去倉庫中獲取符合 1.x.x 形式的最新版本。
  • 獲取模塊實體。上一步會獲取到模塊的壓縮包地址(resolved 字段),npm 會用此地址檢查本地緩存,緩存中有就直接拿,若是沒有則從倉庫下載。
  • 查找該模塊依賴,若是有依賴則回到第1步,若是沒有則中止。
  1. 模塊扁平化(dedupe)

上一步獲取到的是一棵完整的依賴樹,其中可能包含大量重複模塊。好比 A 模塊依賴於 lodash,B 模塊一樣依賴於 lodash。在 npm3 之前會嚴格按照依賴樹的結構進行安裝,所以會形成模塊冗餘。

從 npm3 開始默認加入了一個 dedupe 的過程。它會遍歷全部節點,逐個將模塊放在根節點下面,也就是 node-modules 的第一層。當發現有重複模塊時,則將其丟棄。

這裏須要對重複模塊進行一個定義,它指的是模塊名相同semver 兼容。每一個 semver 都對應一段版本容許範圍,若是兩個模塊的版本容許範圍存在交集,那麼就能夠獲得一個兼容版本,而沒必要版本號徹底一致,這可使更多冗餘模塊在 dedupe 過程當中被去掉。

  1. 安裝模塊

這一步將會更新工程中的 node_modules,並執行模塊中的生命週期函數(按照 preinstall、install、postinstall 的順序)。

  1. 執行工程自身生命週期

當前 npm 工程若是定義了鉤子此時會被執行(按照 install、postinstall、prepublish、prepare 的順序)。

最後一步是生成或更新版本描述文件,npm install 過程完成。

更多解析:github.com/Advanced-Fr…

第 21 題:有如下 3 個判斷數組的方法,請分別介紹它們之間的區別和優劣

Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()

解析:

1. Object.prototype.toString.call()

每個繼承 Object 的對象都有 toString 方法,若是 toString 方法沒有重寫的話,會返回 [Object type],其中 type 爲對象的類型。但當除了 Object 類型的對象外,其餘類型直接使用 toString 方法時,會直接返回都是內容的字符串,因此咱們須要使用call或者apply方法來改變toString方法的執行上下文。

const an = ['Hello','An'];
an.toString(); // "Hello,An"
Object.prototype.toString.call(an); // "[object Array]"
複製代碼

這種方法對於全部基本的數據類型都能進行判斷,即便是 null 和 undefined 。

Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"
複製代碼

Object.prototype.toString.call() 經常使用於判斷瀏覽器內置對象。

2. instanceof

instanceof 的內部機制是經過判斷對象的原型鏈中是否是能找到類型的 prototype

使用 instanceof判斷一個對象是否爲數組,instanceof 會判斷這個對象的原型鏈上是否會找到對應的 Array 的原型,找到返回 true,不然返回 false

[]  instanceof Array; // true
複製代碼

instanceof 只能用來判斷對象類型,原始類型不能夠。而且全部對象類型 instanceof Object 都是 true。

[]  instanceof Object; // true
複製代碼

3. Array.isArray()

  • 功能:用來判斷對象是否爲數組

  • instanceof 與 isArray

    當檢測Array實例時,Array.isArray 優於 instanceof ,由於 Array.isArray 能夠檢測出 iframes

    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    xArray = window.frames[window.frames.length-1].Array;
    var arr = new xArray(1,2,3); // [1,2,3]
    
    // Correctly checking for Array
    Array.isArray(arr);  // true
    Object.prototype.toString.call(arr); // true
    // Considered harmful, because doesn't work though iframes
    arr instanceof Array; // false
    複製代碼
  • Array.isArray()Object.prototype.toString.call()

    Array.isArray()是ES5新增的方法,當不存在 Array.isArray() ,能夠用 Object.prototype.toString.call() 實現。

    if (!Array.isArray) {
      Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
      };
    }
    複製代碼

更多解析:github.com/Advanced-Fr…

第 22 題:介紹下重繪和迴流(Repaint & Reflow),以及如何進行優化

解析:

1. 瀏覽器渲染機制

  • 瀏覽器採用流式佈局模型(Flow Based Layout
  • 瀏覽器會把HTML解析成DOM,把CSS解析成CSSOMDOMCSSOM合併就產生了渲染樹(Render Tree)。
  • 有了RenderTree,咱們就知道了全部節點的樣式,而後計算他們在頁面上的大小和位置,最後把節點繪製到頁面上。
  • 因爲瀏覽器使用流式佈局,對Render Tree的計算一般只須要遍歷一次就能夠完成,但table及其內部元素除外,他們可能須要屢次計算,一般要花3倍於同等元素的時間,這也是爲何要避免使用table佈局的緣由之一

2. 重繪

因爲節點的幾何屬性發生改變或者因爲樣式發生改變而不會影響佈局的,稱爲重繪,例如outline, visibility, colorbackground-color等,重繪的代價是高昂的,由於瀏覽器必須驗證DOM樹上其餘節點元素的可見性。

3. 迴流

迴流是佈局或者幾何屬性須要改變就稱爲迴流。迴流是影響瀏覽器性能的關鍵因素,由於其變化涉及到部分頁面(或是整個頁面)的佈局更新。一個元素的迴流可能會致使了其全部子元素以及DOM中緊隨其後的節點、祖先節點元素的隨後的迴流。

<body>
<div class="error">
    <h4>個人組件</h4>
    <p><strong>錯誤:</strong>錯誤的描述…</p>
    <h5>錯誤糾正</h5>
    <ol>
        <li>第一步</li>
        <li>第二步</li>
    </ol>
</div>
</body>
複製代碼

在上面的HTML片斷中,對該段落(<p>標籤)迴流將會引起強烈的迴流,由於它是一個子節點。這也致使了祖先的迴流(div.errorbody – 視瀏覽器而定)。此外,<h5><ol>也會有簡單的迴流,由於這些節點在DOM中迴流元素以後。大部分的迴流將致使頁面的從新渲染。

迴流一定會發生重繪,重繪不必定會引起迴流。

4. 瀏覽器優化

現代瀏覽器大多都是經過隊列機制來批量更新佈局,瀏覽器會把修改操做放在隊列中,至少一個瀏覽器刷新(即16.6ms)纔會清空隊列,但當你獲取佈局信息的時候,隊列中可能有會影響這些屬性或方法返回值的操做,即便沒有,瀏覽器也會強制清空隊列,觸發迴流與重繪來確保返回正確的值

主要包括如下屬性或方法:

  • offsetTopoffsetLeftoffsetWidthoffsetHeight
  • scrollTopscrollLeftscrollWidthscrollHeight
  • clientTopclientLeftclientWidthclientHeight
  • widthheight
  • getComputedStyle()
  • getBoundingClientRect()

因此,咱們應該避免頻繁的使用上述的屬性,他們都會強制渲染刷新隊列。

5. 減小重繪與迴流

  1. CSS
    • 使用 transform 替代 top
    • 使用 visibility 替換 display: none ,由於前者只會引發重繪,後者會引起迴流
    • 避免使用table佈局,可能很小的一個小改動會形成整個 table 的從新佈局。
    • 儘量在DOM樹的最末端改變class,迴流是不可避免的,但能夠減小其影響。儘量在DOM樹的最末端改變class,能夠限制了迴流的範圍,使其影響儘量少的節點。
    • 避免設置多層內聯樣式,CSS 選擇符從右往左匹配查找,避免節點層級過多。
<div>
       <a> <span></span> </a>
     </div>
     <style>
       span {
         color: red;
       }
       div > a > span {
         color: red;
       }
     </style>
複製代碼

對於第一種設置樣式的方式來講,瀏覽器只須要找到頁面中全部的 span 標籤而後設置顏色,可是對於第二種設置樣式的方式來講,瀏覽器首先須要找到全部的 span 標籤,而後找到 span 標籤上的 a 標籤,最後再去找到 div 標籤,而後給符合這種條件的 span 標籤設置顏色,這樣的遞歸過程就很複雜。因此咱們應該儘量的避免寫過於具體的 CSS 選擇器,而後對於 HTML 來講也儘可能少的添加無心義標籤,保證層級扁平

  • 將動畫效果應用到position屬性爲absolute或fixed的元素上,避免影響其餘元素的佈局,這樣只是一個重繪,而不是迴流,同時,控制動畫速度能夠選擇 requestAnimationFrame,詳見探討 requestAnimationFrame
  • 避免使用CSS表達式,可能會引起迴流。
  • 將頻繁重繪或者回流的節點設置爲圖層,圖層可以阻止該節點的渲染行爲影響別的節點,例如will-changevideoiframe等標籤,瀏覽器會自動將該節點變爲圖層。
  • CSS3 硬件加速(GPU加速),使用css3硬件加速,可讓transformopacityfilters這些動畫不會引發迴流重繪 。可是對於動畫的其它屬性,好比background-color這些,仍是會引發迴流重繪的,不過它仍是能夠提高這些動畫的性能。
  1. JavaScript

    • 避免頻繁操做樣式,最好一次性重寫style屬性,或者將樣式列表定義爲class並一次性更改class屬性。

    • 避免頻繁操做DOM,建立一個documentFragment,在它上面應用全部DOM操做,最後再把它添加到文檔中。

    • 避免頻繁讀取會引起迴流/重繪的屬性,若是確實須要屢次使用,就用一個變量緩存起來。

    • 對具備複雜動畫的元素使用絕對定位,使它脫離文檔流,不然會引發父元素及後續元素頻繁迴流。

更多解析:github.com/Advanced-Fr…

第 23 題:介紹下觀察者模式和訂閱-發佈模式的區別,各自適用於什麼場景

解析:

image

聯繫

發佈-訂閱模式是觀察者模式的一種變體。發佈-訂閱只是把一部分功能抽象成一個獨立的ChangeManager。

意圖

都是某個對象(subject, publisher)改變,使依賴於它的多個對象(observers, subscribers)獲得通知。

區別與適用場景

總的來講,發佈-訂閱模式適合更復雜的場景。

在「一對多」的場景下,發佈者的某次更新只想通知它的部分訂閱者?

在「多對一」或者「多對多」場景下。一個訂閱者依賴於多個發佈者,某個發佈者更新後是否須要通知訂閱者?仍是等全部發布者都更新完畢再通知訂閱者?

這些邏輯均可以放到ChangeManager裏。

更多解析:github.com/Advanced-Fr…

第 24 題:聊聊 Redux 和 Vuex 的設計思想

歡迎在 Issue 區留下你的答案。

交流

進階系列文章彙總以下,內有優質前端資料,以爲不錯點個star。

github.com/yygmind/blo…

我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!

相關文章
相關標籤/搜索