一年半經驗,百度、有贊、阿里前端面試總結

前言

人家都說,前端須要每一年按期出來面面試,衡量一下本身當前的技術水平以及價值,本人17年7月份,畢業到如今都沒出來試過,也沒很想換工做,就出來試試,看看本身水平咋樣。css

如下爲我現場面試時候的一些回答,部分因人而異的問題我就不回答了,回答的都爲參考答案,也有部分錯誤的地方或者很差的地方,有更好的答案的能夠在評論區評論。html

百度 WEB前端工程師 連續五面 全程3約個小時

一面

先完成筆試題前端

  1. 實現一個函數,判斷輸入是否是迴文字符串。
function run(input) {
  if (typeof input !== 'string') return false;
  return input.split('').reverse().join('') === input;
}
  1. 兩種以上方式實現已知或者未知寬度的垂直水平居中。
// 1
.wrapper {
  position: relative;
  .box {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100px;
    height: 100px;
    margin: -50px 0 0 -50px;
  }
}

// 2
.wrapper {
  position: relative;
  .box {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

// 3
.wrapper {
  .box {
    display: flex;
    justify-content:center;
    align-items: center;
    height: 100px;
  }
}

// 4
.wrapper {
  display: table;
  .box {
    display: table-cell;
    vertical-align: middle;
  }
}
  1. 實現效果,點擊容器內的圖標,圖標邊框變成border 1px solid red,點擊空白處重置。
const box = document.getElementById('box');
function isIcon(target) {
  return target.className.includes('icon');
}

box.onclick = function(e) {
  e.stopPropagation();
  const target = e.target;
  if (isIcon(target)) {
    target.style.border = '1px solid red';
  }
}
const doc = document;
doc.onclick = function(e) {
  const children = box.children;
  for(let i = 0; i < children.length; i++) {
    if (isIcon(children[i])) {
      children[i].style.border = 'none';
    }
  }
}
  1. 請簡單實現雙向數據綁定mvvm。
<input id="input"/>
const data = {};
const input = document.getElementById('input');
Object.defineProperty(data, 'text', {
  set(value) {
    input.value = value;
    this.value = value;
  }
});
input.onchange = function(e) {
  data.text = e.target.value;
}
  1. 實現Storage,使得該對象爲單例,並對localStorage進行封裝設置值setItem(key,value)和getItem(key)
var instance = null;
class Storage {
  static getInstance() {
    if (!instance) {
      instance = new Storage();
    }
    return this.instance;
  }
  setItem = (key, value) => localStorage.setItem(key, value),
  getItem = key => localStorage.getItem(key)
}
Q1 你的技術棧主要是react,那你說說你用react有什麼坑點?

一、JSX作表達式判斷時候,須要強轉爲boolean類型,如:java

render() {
  const b = 0;
  return <div>
    {
      !!b && <div>這是一段文本</div>
    }
  </div>
}

若是不使用 !!b 進行強轉數據類型,會在頁面裏面輸出 0。node

二、儘可能不要在 componentWillReviceProps 裏使用 setState,若是必定要使用,那麼須要判斷結束條件,否則會出現無限重渲染,致使頁面崩潰。(實際不是componentWillReviceProps會無限重渲染,而是componentDidUpdate)react

三、給組件添加ref時候,儘可能不要使用匿名函數,由於當組件更新的時候,匿名函數會被當作新的prop處理,讓ref屬性接受到新函數的時候,react內部會先清空ref,也就是會以null爲回調參數先執行一次ref這個props,而後在以該組件的實例執行一次ref,因此用匿名函數作ref的時候,有的時候去ref賦值後的屬性會取到null。
詳情見jquery

四、遍歷子節點的時候,不要用 index 做爲組件的 key 進行傳入。webpack

Q2 我如今有一個button,要用react在上面綁定點擊事件,要怎麼作?
class Demo {
  render() {
    return <button onClick={(e) => {
      alert('我點擊了按鈕')
    }}>
      按鈕
    </button>
  }
}
Q3 接上一個問題,你以爲你這樣設置點擊事件會有什麼問題嗎?

因爲onClick使用的是匿名函數,全部每次重渲染的時候,會把該onClick當作一個新的prop來處理,會將內部緩存的onClick事件進行從新賦值,因此相對直接使用函數來講,可能有一點的性能降低(我的認爲)。es6

修改web

class Demo {

  onClick = (e) => {
    alert('我點擊了按鈕')
  }

  render() {
    return <button onClick={this.onClick}>
      按鈕
    </button>
  }
}

固然你在內部聲明的不是箭頭函數,而後你可能須要在設置onClick的時候使用bind綁定上下文,這樣的效果和先前的使用匿名函數差很少,由於bind會返回新的函數,也會被react認爲是一個新的prop。

Q4 你說說event loop吧

首先,js是單線程的,主要的任務是處理用戶的交互,而用戶的交互無非就是響應DOM的增刪改,使用事件隊列的形式,一次事件循環只處理一個事件響應,使得腳本執行相對連續,因此有了事件隊列,用來儲存待執行的事件,那麼事件隊列的事件從哪裏被push進來的呢。那就是另一個線程叫事件觸發線程作的事情了,他的做用主要是在定時觸發器線程、異步HTTP請求線程知足特定條件下的回調函數push到事件隊列中,等待js引擎空閒的時候去執行,固然js引擎執行過程當中有優先級之分,首先js引擎在一次事件循環中,會先執行js線程的主任務,而後會去查找是否有微任務microtask(promise),若是有那就優先執行微任務,若是沒有,在去查找宏任務macrotask(setTimeout、setInterval)進行執行。

Q5 說說事件流吧

事件流分爲兩種,捕獲事件流和冒泡事件流。
捕獲事件流從根節點開始執行,一直往子節點查找執行,直到查找執行到目標節點。
冒泡事件流從目標節點開始執行,一直往父節點冒泡查找執行,直到查到到根節點。

DOM事件流分爲三個階段,一個是捕獲節點,一個是處於目標節點階段,一個是冒泡階段。

Q6 我如今有一個進度條,進度條中間有一串文字,當個人進度條覆蓋了文字以後,文字要與進度條反色,怎麼實現?

。。。當時我給的是js的方案,在進度條寬度變化的時候,計算蓋過每個文字的50%,若是超過,設置文字相反顏色。

固然css也有對應的方案,也就是 mix-blend-mode,我並無接觸過。

對應html也有對應方案,也就設置兩個相同位置可是顏色相反的dom結構在重疊在一塊兒,頂層覆蓋底層,最頂層的進度條取overflow爲hidden,其寬度就爲進度。

二面

Q1 你爲何要離開上一家公司?

-

Q2 你以爲理想的前端地位是什麼?

-

Q3 那你意識到問題所在,你又嘗試過解決問題嗎?

-

三面

Q1 說一下你上一家公司的一個總體開發流程吧

-

Q2 react 的虛擬dom是怎麼實現的

首先說說爲何要使用Virturl DOM,由於操做真實DOM的耗費的性能代價過高,因此react內部使用js實現了一套dom結構,在每次操做在和真實dom以前,使用實現好的diff算法,對虛擬dom進行比較,遞歸找出有變化的dom節點,而後對其進行更新操做。爲了實現虛擬DOM,咱們須要把每一種節點類型抽象成對象,每一種節點類型有本身的屬性,也就是prop,每次進行diff的時候,react會先比較該節點類型,假如節點類型不同,那麼react會直接刪除該節點,而後直接建立新的節點插入到其中,假如節點類型同樣,那麼會比較prop是否有更新,假若有prop不同,那麼react會斷定該節點有更新,那麼重渲染該節點,而後在對其子節點進行比較,一層一層往下,直到沒有子節點。

Q3 react 的渲染過程當中,兄弟節點之間是怎麼處理的?也就是key值不同的時候。

一般咱們輸出節點的時候都是map一個數組而後返回一個ReactNode,爲了方便react內部進行優化,咱們必須給每個reactNode添加key,這個key prop在設計值處不是給開發者用的,而是給react用的,大概的做用就是給每個reactNode添加一個身份標識,方便react進行識別,在重渲染過程當中,若是key同樣,若組件屬性有所變化,則react只更新組件對應的屬性;沒有變化則不更新,若是key不同,則react先銷燬該組件,而後從新建立該組件。

Q4 我如今有一個數組[1,2,3,4],請實現算法,獲得這個數組的全排列的數組,如[2,1,3,4],[2,1,4,3]。。。。你這個算法的時間複雜度是多少

這個我沒寫出來,大概給了個思路,將每個數組拆除倆個小數組進行求它的全排列,而後獲得的結果互相之間又進行全排列,而後把最後的結果鏈接起來。。。

感興趣的同窗見數組全排列

Q5 我如今有一個揹包,容量爲m,而後有n個貨物,重量分別爲w1,w2,w3...wn,每一個貨物的價值是v1,v2,v3...vn,w和v沒有任何關係,請求揹包能裝下的最大價值。

這個我也沒寫出來,也給了個思路,首先使用Q4的方法獲得貨物重量數組的全組合(包括拆分紅小數組的全組合),而後計算每個組合的價值,並進行排序,而後遍歷數組,找到價值較高切恰好能裝進揹包m的組合。

本題動態規劃面試題,感興趣的同窗請自行百度或者谷歌。

四面

Q1 請說一下你的上一家公司的研發發佈流程。

-

Q2 你說一下webpack的一些plugin,怎麼使用webpack對項目進行優化。

正好最近在作webpack構建優化和性能優化的事兒,當時吹了大概15~20分鐘吧,插件請見webpack插件概括總結

構建優化
一、減小編譯體積 ContextReplacementPugin、IgnorePlugin、babel-plugin-import、babel-plugin-transform-runtime。
二、並行編譯 happypack、thread-loader、uglifyjsWebpackPlugin開啓並行
三、緩存 cache-loader、hard-source-webpack-plugin、uglifyjsWebpackPlugin開啓緩存、babel-loader開啓緩存
四、預編譯 dllWebpackPlugin && DllReferencePlugin、auto-dll-webapck-plugin

性能優化
一、減小編譯體積 Tree-shaking、Scope Hositing。
二、hash緩存 webpack-md5-plugin
三、拆包 splitChunksPlugin、import()、require.ensure

Q3 es6 class 的new實例和es5的new實例有什麼區別

這個我以爲是同樣的(當時由於不多看babel編譯以後的結果),面試官說不同。。。後來我看了一下babel的編譯結果,發現只是類的方法聲明的過程不同而已,最後new的結果是同樣的。。。具體答案如今我也不知道。。。

Q4 看你簡歷上寫了canvas,你說一下爲何canvas的圖片爲何過有跨域問題。

canvas圖片爲何跨域我不知道,至今沒查出來,也差很少,大概跨域緣由和瀏覽器跨域的緣由是同樣的吧。

Q5 我如今有一個canvas,上面隨機布着一些黑塊,請實現方法,計算canvas上有多少個黑塊。

使用getImageData獲取像素數組,而後遍歷數組,把在遍歷節點的過程當中,查看節點上下左右的像素顏色是否相同,若是相同,而後設置標識,最後groupBy一下全部像素。(這是我當時的方案)

其餘更好的答案見地址

Q6 請手寫實現一個promise

這個就不寫了,詳情見promise實現原理

注:四面是一個超級可愛的小姐姐,電腦給我讓我寫完以後,我說我寫得差很少了,而後電腦給她,而後她居然默默的在看個人代碼,嘗試尋找個人思路,也沒有問我實現思路是啥,而後我就問她,你不該該是讓我給你解釋個人代碼思路嗎。。。你居然在嘗試尋找個人思路,我本身都不知道我本身是思路是啥。。。而後我兩都笑了,哈哈哈。最後結束的時候我說我午餐還沒吃,她還叫了另一個小哥哥先帶了下去吃飯,真是一個善良的小姐姐,很是感謝。

五面

Q1 你說一下你的技術有什麼特色

-

Q2 說一下你以爲你最得意的一個項目?你這個項目有什麼缺陷,弊端嗎?

-

Q3 如今有那麼一個團隊,假如讓你來作技術架構,你會怎麼作?

考慮到團隊每個前端的技術棧可能不一致,這個時候我可能選擇微前端架構,讓每一個人負責的模塊能夠單獨開發,單獨部署,單獨回滾,不依賴於其餘項目模塊,在儘量的狀況下節約團隊成員之間的學習成本,固然這確定也有缺點,那就是每一個模塊都須要一個前端項目,單獨部署,單獨回滾無疑也加大了運維成本。

Q4 說一下你上一家公司的主要業務流程,你參與到其中了嗎?

-

杭州有贊

一面 WEB前端工程師 電話面 全程43分鐘

Q1 自我介紹

-

Q2 說說從輸入URL到看到頁面發生的全過程,越詳細越好。
  1. 首先瀏覽器主進程接管,開了一個下載線程。
  2. 而後進行HTTP請求(DNS查詢、IP尋址等等),中間會有三次捂手,等待響應,開始下載響應報文。
  3. 將下載完的內容轉交給Renderer進程管理。
  4. Renderer進程開始解析css rule tree和dom tree,這兩個過程是並行的,因此通常我會把link標籤放在頁面頂部。
  5. 解析繪製過程當中,當瀏覽器遇到link標籤或者script、img等標籤,瀏覽器會去下載這些內容,遇到時候緩存的使用緩存,不適用緩存的從新下載資源。
  6. css rule tree和dom tree生成完了以後,開始合成render tree,這個時候瀏覽器會進行layout,開始計算每個節點的位置,而後進行繪製。
  7. 繪製結束後,關閉TCP鏈接,過程有四次揮手。
Q3 你剛剛說了三次握手,四次揮手,那你描述一下?

本人對計算機網絡的這些概念一直不是很熟悉,因此這個問題回答不會,這裏mark下文章,感興趣的同窗查看地址

Q4 剛剛Q2中說的CSS和JS的位置會影響頁面效率,爲何?

css在加載過程當中不會影響到DOM樹的生成,可是會影響到Render樹的生成,進而影響到layout,因此通常來講,style的link標籤須要儘可能放在head裏面,由於在解析DOM樹的時候是自上而下的,而css樣式又是經過異步加載的,這樣的話,解析DOM樹下的body節點和加載css樣式能儘量的並行,加快Render樹的生成的速度。

js腳本應該放在底部,緣由在於js線程與GUI渲染線程是互斥的關係,若是js放在首部,當下載執行js的時候,會影響渲染行程繪製頁面,js的做用主要是處理交互,而交互必須得先讓頁面呈現才能進行,因此爲了保證用戶體驗,儘可能讓頁面先繪製出來。

Q5 如今有一個函數A和函數B,請你實現B繼承A
// 方式1
function B(){}
function A(){}
B.prototype = new A();

// 方式2
function A(){}
function B(){
  A.call(this);
}

// 方式3
function B(){}
function A(){}
B.prototype = new A();

function B(){
  A.call(this);
}
Q6 剛剛你在Q5中說的幾種繼承的方式,分別說說他們的優缺點

方式1:簡單易懂,可是沒法實現多繼承,父類新增原型方法/原型屬性,子類都能訪問到
方式2:能夠實現多繼承,可是隻能繼承父類的實例屬性和方法,不能繼承原型屬性/方法
方式3:能夠繼承實例屬性/方法,也能夠繼承原型屬性/方法,可是示例了兩個A的構造函數

Q7 說說CSS中幾種垂直水平居中的方式

參考前面百度一面筆試題Q2

Q8 Q7中說的flex佈局,垂直水平居中必須知道寬度嗎?

是的,必須知道高度(腦子進水了回答了必須知道,其實答案是不須要知道高度的)

Q9 描述一下this

this,函數執行的上下文,能夠經過apply,call,bind改變this的指向。對於匿名函數或者直接調用的函數來講,this指向全局上下文(瀏覽器爲window,nodejs爲global),剩下的函數調用,那就是誰調用它,this就指向誰。固然還有es6的箭頭函數,箭頭函數的指向取決於該箭頭函數聲明的位置,在哪裏聲明,this就指向哪裏。

Q10 說一下瀏覽器的緩存機制

瀏覽器緩存機制有兩種,一種爲強緩存,一種爲協商緩存。
對於強緩存,瀏覽器在第一次請求的時候,會直接下載資源,而後緩存在本地,第二次請求的時候,直接使用緩存。
對於協商緩存,第一次請求緩存且保存緩存標識與時間,重複請求向服務器發送緩存標識和最後緩存時間,服務端進行校驗,若是失效則使用緩存。

強緩存方案
Exprires:服務端的響應頭,第一次請求的時候,告訴客戶端,該資源何時會過時。Exprires的缺陷是必須保證服務端時間和客戶端時間嚴格同步。
Cache-control:max-age,表示該資源多少時間後過時,解決了客戶端和服務端時間必須同步的問題,

協商緩存方案
If-None-Match/ETag:緩存標識,對比緩存時使用它來標識一個緩存,第一次請求的時候,服務端會返回該標識給客戶端,客戶端在第二次請求的時候會帶上該標識與服務端進行對比並返回If-None-Match標識是否表示匹配。
Last-modified/If-Modified-Since:第一次請求的時候服務端返回Last-modified代表請求的資源上次的修改時間,第二次請求的時候客戶端帶上請求頭If-Modified-Since,表示資源上次的修改時間,服務端拿到這兩個字段進行對比。

Q11 ETag是這個字符串是怎麼生成的?

沒答出來,我當時猜是根據文件內容或者最後修改時間進行的加密算法。其實官方沒有明確指定生成ETag值的方法。 一般,使用內容的散列,最後修改時間戳的哈希值,或簡單地使用版本號。

Q12 如今要你完成一個Dialog組件,說說你設計的思路?它應該有什麼功能?
  1. 該組件須要提供hook指定渲染位置,默認渲染在body下面。
  2. 而後改組件能夠指定外層樣式,如寬度等
  3. 組件外層還須要一層mask來遮住底層內容,點擊mask能夠執行傳進來的onCancel函數關閉Dialog。
  4. 另外組件是可控的,須要外層傳入visible表示是否可見。
  5. 而後Dialog可能須要自定義頭head和底部footer,默認有頭部和底部,底部有一個確認按鈕和取消按鈕,確認按鈕會執行外部傳進來的onOk事件,而後取消按鈕會執行外部傳進來的onCancel事件。
  6. 當組件的visible爲true時候,設置body的overflow爲hidden,隱藏body的滾動條,反之顯示滾動條。
  7. 組件高度可能大於頁面高度,組件內部須要滾動條。
  8. 只有組件的visible有變化且爲ture時候,才重渲染組件內的全部內容。
Q13 你以爲你作過的你以爲最值得炫耀的項目?

螞蟻金服-體驗技術部 資深數據可視化研發工程師

一面 電話面 全程1小時24分鐘

Q1 描述一下你最近作的可視化的項目

-

Q2 剛剛說的java調用js離線生成數據報告?java調用js的promise異步返回結果怎麼實現的?

使用java的js引擎Nashorn,Nashorn不支持事件隊列,是要引進polyfill,而後java調用js方法得到java的promise對象,而後在調用該對象的then方法,回調函數爲java中的某各種的某個方法,而後while一個表示是否已執行回調的變量,若是未執行,則讓java主線程sleep,若是已經執行,則跳出循環,表示是否已執行回調的變量在傳入promise的回調函數中設置更改。詳情代碼見地址

Q3 說說svg和canvas各自的優缺點?

共同點:都是有效的圖形工具,對於數據較小的狀況下,都很又高的性能,它們都使用 JavaScript 和 HTML;它們都遵照萬維網聯合會 (W3C) 標準。

svg優勢:
矢量圖,不依賴於像素,無限放大後不會失真。
以dom的形式表示,事件綁定由瀏覽器直接分發到節點上。
svg缺點:
dom形式,涉及到動畫時候須要更新dom,性能較低。

canvas優勢:
定製型更強,能夠繪製繪製本身想要的東西。
非dom結構形式,用JavaScript進行繪製,涉及到動畫性能較高。
canvas缺點:
事件分發由canvas處理,繪製的內容的事件須要本身作處理。
依賴於像素,沒法高效保真,畫布較大時候性能較低。

Q4 你剛剛說的canvas渲染較大畫布的時候性能會較低?爲何?

由於canvas依賴於像素,在繪製過程當中是一個一個像素去繪製的,當畫布足夠大,像素點也就會足夠多,那麼想能就會足夠低。

Q6 假設我如今有5000個圓,徹底繪製出來,點擊某一個圓,該圓高亮,另外4999個圓設爲半透明,分別說說用svg和canvas怎麼實現?

首先,從數據出發,咱們的每一個圓是一個數據,這個數據有圓的x、y、radius、isHighlight若是是svg,直接渲染節點便可,而後往節點上邊綁定點擊事件,點擊改變全部數據的高亮屬性(必須同步執行完成),而後讓瀏覽器進行繪製。若是是canvas,咱們須要本身綁定事件到canvans標籤上,而後點擊的時候判斷點擊的位置是否在圓內,若是在某個圓內,則更新全部數據的高亮屬性,以後在進行一次性繪製。

Q7 剛剛說的canvas的點擊事件,怎麼樣實現?假如不是圓,這些圖形是正方形、長方形、規則圖形、不規則圖形呢。

針對於每個形狀,將其抽象成shape類,每個類有本身的方法isPointInSide來判斷節點是否在圖形內,對於不規則圖形,當作矩形處理,點擊的時候執行該方法判斷點擊位置是否在圖形內。

Q8 那假如個人圖形可能有變形、放大、偏移、旋轉的需求呢?你的這個isPointInSide怎麼處理?

這個我答不出來,據面試官提示,好像有相應的API處理變形、旋轉、放大等等以後的位置映射關係。

Q9 那個這個canvas的點擊事件,點擊的時候怎麼樣快速的從這5000個圓中找到你點擊的那個圓(不徹底遍歷5000個節點)?

能夠經過預查找的形式,當鼠標劃過的時候預先查找到鼠標附近的一些節點,當點擊的時候在從這些預先篩選好的節點裏查找點擊下來的節點,固然這個方法的前提是不能影響js主線程的執行,必須是異步的形式。

Q10 那你用過@antv/g6,裏面有一個tree,說說你大學時候接觸到的tree的數據結構是怎麼實現的?

畢業一年多,tree的結構大概忘記了,我當時是這麼回答的:

大學使用的是C++學的數據結構,是用指針的形式,首先有一個根節點,根節點裏有一個指針數組指向它的全部子節點,而後每個子節點也是,擁有着子節點的指針數組,一層一層往下,直到爲葉子節點,指針數組指向爲空。

Q11 還記得二叉樹嗎?描述二叉樹的幾種遍歷方式?

先序遍歷:若二叉樹非空,訪問根結點,遍歷左子樹,遍歷右子樹。
中序遍歷:若二叉樹非空,遍歷左子樹;訪問根結點;遍歷右子樹。
後序遍歷:若二叉樹非空,遍歷左子樹;遍歷右子樹;訪問根結點。

全部遍歷是以遞歸的形似,直到沒有子節點。

Q12 說說你記得的全部的排序,他們的原理是什麼?

冒泡排序:雙層遍歷,對比先後兩個節點,若是知足條件,位置互換,直到遍歷結束。
快速排序:去數組中間的那一個數,而後遍歷全部數,小於該數的push到一個數組,大於該數的push到另一個數組,而後遞歸去排序這兩個數組,最後將全部結果鏈接起來。
選擇排序:聲明一個數組,每次去輸入數組裏面找數組中的最大值或者最小值,取出來後push到聲明的數組中,直到輸入數組爲空。

Q13 說一下你以爲你作過的最複雜的項目?中間遇到的困難,以及你是怎麼解決的?

面試官:我這邊問題差很少問完了,你還有什麼問題?

我:很驚訝今天全都是問可視化相關的,沒怎麼問js,css,html。

面試官:那咱們繼續吧

我:。。。


Q14 那給我介紹一下react吧(面試官是作可視化開發的,根本不懂react)

之前咱們沒有jquery的時候,咱們大概的流程是從後端經過ajax獲取到數據而後使用jquery生成dom結果真後更新到頁面當中,可是隨着業務發展,咱們的項目可能會愈來愈複雜,咱們每次請求到數據,或則數據有更改的時候,咱們又須要從新組裝一次dom結構,而後更新頁面,這樣咱們手動同步dom和數據的成本就愈來愈高,並且頻繁的操做dom,也使我咱們頁面的性能慢慢的下降。

這個時候mvvm出現了,mvvm的雙向數據綁定可讓咱們在數據修改的同時同步dom的更新,dom的更新也能夠直接同步咱們數據的更改,這個特定能夠大大下降咱們手動去維護dom更新的成本,mvvm爲react的特性之一,雖然react屬於單項數據流,須要咱們手動實現雙向數據綁定。

有了mvvm還不夠,由於若是每次有數據作了更改,而後咱們都全量更新dom結構的話,也沒辦法解決咱們頻繁操做dom結構(下降了頁面性能)的問題,爲了解決這個問題,react內部實現了一套虛擬dom結構,也就是用js實現的一套dom結構,他的做用是講真實dom在js中作一套緩存,每次有數據更改的時候,react內部先使用算法,也就是鼎鼎有名的diff算法對dom結構進行對比,找到那些咱們須要新增、更新、刪除的dom節點,而後一次性對真實DOM進行更新,這樣就大大下降了操做dom的次數。

那麼diff算法是怎麼運做的呢,首先,diff針對類型不一樣的節點,會直接斷定原來節點須要卸載而且用新的節點來裝載卸載的節點的位置;針對於節點類型相同的節點,會對比這個節點的全部屬性,若是節點的全部屬性相同,那麼斷定這個節點不須要更新,若是節點屬性不相同,那麼會斷定這個節點須要更新,react會更新並重渲染這個節點。

react設計之初是主要負責UI層的渲染,雖然每一個組件有本身的state,state表示組件的狀態,當狀態須要變化的時候,須要使用setState更新咱們的組件,可是,咱們想經過一個組件重渲染它的兄弟組件,咱們就須要將組件的狀態提高到父組件當中,讓父組件的狀態來控制這兩個組件的重渲染,當咱們組件的層次愈來愈深的時候,狀態須要一直往下傳,無疑加大了咱們代碼的複雜度,咱們須要一個狀態管理中心,來幫咱們管理咱們狀態state。

這個時候,redux出現了,咱們能夠將全部的state交給redux去管理,當咱們的某一個state有變化的時候,依賴到這個state的組件就會進行一次重渲染,這樣就解決了咱們的咱們須要一直把state往下傳的問題。redux有action、reducer的概念,action爲惟一修改state的來源,reducer爲惟一肯定state如何變化的入口,這使得redux的數據流很是規範,同時也暴露出了redux代碼的複雜,原本那麼簡單的功能,卻須要完成那麼多的代碼。

後來,社區就出現了另一套解決方案,也就是mobx,它推崇代碼簡約易懂,只須要定義一個可觀測的對象,而後哪一個組價使用到這個可觀測的對象,而且這個對象的數據有更改,那麼這個組件就會重渲染,並且mobx內部也作好了是否重渲染組件的生命週期shouldUpdateComponent,不建議開發者進行更改,這使得咱們使用mobx開發項目的時候能夠簡單快速的完成不少功能,連redux的做者也推薦使用mobx進行項目開發。可是,隨着項目的不斷變大,mobx也不斷暴露出了它的缺點,就是數據流太隨意,出了bug以後很差追溯數據的流向,這個缺點正好體現出了redux的優勢所在,因此針對於小項目來講,社區推薦使用mobx,對大項目推薦使用redux。

Q15 假如我一個組件有一個狀態count爲1,而後我在componentDidMount()裏面執行執行了兩次this.setState({count: ++this.state.count}),而後又執行了兩次setTimeout(() => { this.setState({count: ++this.state.count}) }, 0),最後count爲多少?爲何?

count爲4,由於第二次執行setState的時候,取不到第一次++this.state.count的結果,react在一輪生命週期結束後纔會更新內部的state,若是在一輪生命週期內屢次使用了setState,react內部會有一個字段isBatchUpdate標識本次更新爲批量更新,而後在最後render的時候將全部setState的結果提交到state中,一次性進行更新,而且把isBatchUpdate這個字段設置爲false。

針對於兩次setTimeout,js引擎會把這兩個setState丟到事件隊列中,等待js空閒了去執行,而咱們的渲染函數render是同步執行的(react16版本默認沒有開啓異步渲染),因此等咱們render執行徹底,也就是咱們的state被同步完後,在取事件隊列裏面的setState進行執行,setTimeout的第二個setState也是同樣的,因此最後結果是4。

備註:這個count的答案彷佛有疑問,寫了個demo,答案並非4,Demo地址 https://jsfiddle.net/yacan8/5gspLrda/13/

Q16 說一下你以爲你作過的最值得你說的吧

-

最後

這幾輪面試的面試官都很是和善好交流,百度的五輪面試不知道過了沒有,只記得五面的面試官說,你稍等一下,我去問一下其餘人對你還有什麼其餘要求,而後過了一下子HR就喊我先回去了,叫我等HR面的消息,若是沒經過,也不會在聯繫我了,已通過了四天了,希望後面有消息吧。而後有贊、螞蟻金服的兩個一面都過了,由於每次面完試面試官問我還有什麼問題嗎?我都會詢問一下本次面試面試官對個人評論是啥。

後續傳送門 -> 記一次前端面試的全過程

相關文章
相關標籤/搜索