大廠前端高頻面試問題與答案精選

近日,GitHub上一位名為木易楊(yygmind)的開發者,在 GitHub 中建了一個名為Advanced-Frontend/Daily-Interview-Question項目,該項目每天會更新一道前端大廠面試題,并邀請開發者在issue區中作答,以下是我們從該項目中挑選的9道題和答案,希望能給大家一些幫助。

GitHub鏈接:
https://github.com/Advanced-Frontend/Daily-Interview-Question

1.寫 React/Vue 項目時為什么要在組件中寫 key,其作用是什么?

key的作用是為了在diff算法執行時更快的找到對應的節點,提高diff速度。

vue和react都是采用diff算法來對比新舊虛擬節點,從而更新節點。在vue的diff函數中。可以先了解一下diff算法。

在交叉對比的時候,當新節點跟舊節點頭尾交叉對比沒有結果的時候,會根據新節點的key去對比舊節點數組中的key,從而找到相應舊節點(這里對應的是一個key =\u0026gt; index 的map映射)。如果沒找到就認為是一個新增節點。而如果沒有key,那么就會采用一種遍歷查找的方式去找到對應的舊節點。一種一個map映射,另一種是遍歷查找。相比而言。map映射的速度更快。

vue部分源碼如下:

// vue項目  src/core/vdom/patch.js  -488行// oldCh 是一個舊虛擬節點數組,  if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)        idxInOld = isDef(newStartVnode.key)          ? oldKeyToIdx[newStartVnode.key]          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)

創建map函數:

function createKeyToOldIdx (children, beginIdx, endIdx) {  let i, key  const map = {}  for (i = beginIdx; i \u0026lt;= endIdx; ++i) {    key = children[i].key    if (isDef(key)) map[key] = i  }  return map}

遍歷尋找:

// sameVnode 是對比新舊節點是否相同的函數 function findIdxInOld (node, oldCh, start, end) {    for (let i = start; i \u0026lt; end; i++) {      const c = oldCh[i]            if (isDef(c) \u0026amp;\u0026amp; sameVnode(node, c)) return i    }  }

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/1

2. 解析[‘1’, ‘2’, ‘3’].map(parseInt)

第一眼看到這個題目的時候,腦海跳出的答案是 [1, 2, 3],但是真正的答案是[1, NaN, NaN]

  • 首先讓我們回顧一下,map函數的第一個參數callback:
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])

這個callback一共可以接收三個參數,其中第一個參數代表當前被處理的元素,而第二個參數代表該元素的索引。

  • 而parseInt則是用來解析字符串的,使字符串成為指定基數的整數。

parseInt(string, radix)接收兩個參數,第一個表示被處理的值(字符串),第二個表示為解析時的基數。

  • 了解這兩個函數后,我們可以模擬一下運行情況
  1. parseInt(‘1’, 0) //radix為0時,且string參數不以“0x”和“0”開頭時,按照10為基數處理。這個時候返回1;

  2. parseInt(‘2’, 1) //基數為1(1進制)表示的數中,最大值小于2,所以無法解析,返回NaN;

  3. parseInt(‘3’, 2) //基數為2(2進制)表示的數中,最大值小于3,所以無法解析,返回NaN。

  • map函數返回的是一個數組,所以最后結果為[1, NaN, NaN]。

  • 最后附上MDN上對于這兩個函數的鏈接,具體參數大家可以到里面看:

  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt

  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map

本題鏈接:

https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/4

3.什么是防抖和節流?有什么區別?如何實現?

  1. 防抖

觸發高頻事件后n秒內函數只會執行一次,如果n秒內高頻事件再次被觸發,則重新計算時間;

  • 思路:

每次觸發事件時都取消之前的延時調用方法:

function debounce(fn) {      let timeout = null; // 創建一個標記用來存放定時器的返回值      return function () {        clearTimeout(timeout); // 每當用戶輸入的時候把前一個 setTimeout clear 掉        timeout = setTimeout(() =\u0026gt; { // 然后又創建一個新的 setTimeout, 這樣就能保證輸入字符后的 interval 間隔內如果還有字符輸入的話,就不會執行 fn 函數          fn.apply(this, arguments);        }, 500);      };    }    function sayHi() {      console.log('防抖成功');    }    var inp = document.getElementById('inp');    inp.addEventListener('input', debounce(sayHi)); // 防抖

2.節流

高頻事件觸發,但在n秒內只會執行一次,所以節流會稀釋函數的執行頻率。

  • 思路:

每次觸發事件時都判斷當前是否有等待執行的延時函數。

function throttle(fn) {      let canRun = true; // 通過閉包保存一個標記      return function () {        if (!canRun) return; // 在函數開頭判斷標記是否為true,不為true則return        canRun = false; // 立即設置為false        setTimeout(() =\u0026gt; { // 將外部傳入的函數的執行放在setTimeout中          fn.apply(this, arguments);          // 最后在setTimeout執行完畢后再把標記設置為true(關鍵)表示可以執行下一次循環了。當定時器沒有執行的時候標記永遠是false,在開頭被return掉          canRun = true;        }, 500);      };    }    function sayHi(e) {      console.log(e.target.innerWidth, e.target.innerHeight);    }    window.addEventListener('resize', throttle(sayHi));

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/5

4.介紹下 Set、Map、WeakSet 和 WeakMap 的區別?

Set

  • 成員唯一、無序且不重復;

  • [value, value],鍵值與鍵名是一致的(或者說只有鍵值,沒有鍵名);

  • 可以遍歷,方法有:add、delete、has。

WeakSet

  • 成員都是對象;

  • 成員都是弱引用,可以被垃圾回收機制回收,可以用來保存DOM節點,不容易造成內存泄漏;

  • 不能遍歷,方法有add、delete、has。

Map

  • 本質上是鍵值對的集合,類似集合;

  • 可以遍歷,方法很多,可以跟各種數據格式轉換。

WeakMap

  • 只接受對象最為鍵名(null除外),不接受其他類型的值作為鍵名;

  • 鍵名是弱引用,鍵值可以是任意的,鍵名所指向的對象可以被垃圾回收,此時鍵名是無效的;

  • 不能遍歷,方法有get、set、has、delete。

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/6

5.介紹下深度優先遍歷和廣度優先遍歷,如何實現?

深度優先遍歷(DFS)

深度優先遍歷(Depth-First-Search),是搜索算法的一種,它沿著樹的深度遍歷樹的節點,盡可能深地搜索樹的分支。當節點v的所有邊都已被探尋過,將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已探尋源節點到其他所有節點為止,如果還有未被發現的節點,則選擇其中一個未被發現的節點為源節點并重復以上操作,直到所有節點都被探尋完成。

簡單的說,DFS就是從圖中的一個節點開始追溯,直到最后一個節點,然后回溯,繼續追溯下一條路徑,直到到達所有的節點,如此往復,直到沒有路徑為止。

DFS 可以產生相應圖的拓撲排序表,利用拓撲排序表可以解決很多問題,例如最大路徑問題。一般用堆數據結構來輔助實現DFS算法。

注意:深度DFS屬于盲目搜索,無法保證搜索到的路徑為最短路徑,也不是在搜索特定的路徑,而是通過搜索來查看圖中有哪些路徑可以選擇。

步驟:

  • 訪問頂點v;

  • 依次從v的未被訪問的鄰接點出發,對圖進行深度優先遍歷;直至圖中和v有路徑相通的頂點都被訪問;

  • 若此時途中尚有頂點未被訪問,則從一個未被訪問的頂點出發,重新進行深度優先遍歷,直到所有頂點均被訪問過為止。

實現:

Graph.prototype.dfs = function() {    var marked = []    for (var i=0; i\u0026lt;this.vertices.length; i++) {        if (!marked[this.vertices[i]]) {            dfsVisit(this.vertices[i])        }    }        function dfsVisit(u) {        let edges = this.edges        marked[u] = true        console.log(u)        var neighbors = edges.get(u)        for (var i=0; i\u0026lt;neighbors.length; i++) {            var w = neighbors[i]            if (!marked[w]) {                dfsVisit(w)            }        }    }}

測試:

graph.dfs()// 1// 4// 3// 2// 5

測試成功。

廣度優先遍歷(BFS)

廣度優先遍歷(Breadth-First-Search)是從根節點開始,沿著圖的寬度遍歷節點,如果所有節點均被訪問過,則算法終止,BFS 同樣屬于盲目搜索,一般用隊列數據結構來輔助實現BFS。

BFS從一個節點開始,嘗試訪問盡可能靠近它的目標節點。本質上這種遍歷在圖上是逐層移動的,首先檢查最靠近第一個節點的層,再逐漸向下移動到離起始節點最遠的層

步驟:

  • 創建一個隊列,并將開始節點放入隊列中;

  • 若隊列非空,則從隊列中取出第一個節點,并檢測它是否為目標節點;

    • 若是目標節點,則結束搜尋,并返回結果;
    • 若不是,則將它所有沒有被檢測過的字節點都加入隊列中;
  • 若隊列為空,表示圖中并沒有目標節點,則結束遍歷。

實現:

Graph.prototype.bfs = function(v) {    var queue = [], marked = []    marked[v] = true    queue.push(v) // 添加到隊尾    while(queue.length \u0026gt; 0) {        var s = queue.shift() // 從隊首移除        if (this.edges.has(s)) {            console.log('visited vertex: ', s)        }        let neighbors = this.edges.get(s)        for(let i=0;i\u0026lt;neighbors.length;i++) {            var w = neighbors[i]            if (!marked[w]) {                marked[w] = true                queue.push(w)            }        }    }}

測試:

graph.bfs(1)// visited vertex:  1// visited vertex:  4// visited vertex:  3// visited vertex:  2// visited vertex:  5

測試成功。

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/9

6.異步筆試題

請寫出下面代碼的運行結果:

// 今日頭條面試題async function async1() {    console.log('async1 start')    await async2()    console.log('async1 end')}async function async2() {    console.log('async2')}console.log('script start')setTimeout(function () {    console.log('settimeout')})async1()new Promise(function (resolve) {    console.log('promise1')    resolve()}).then(function () {    console.log('promise2')})console.log('script end')

題目的本質,就是考察setTimeoutpromiseasync await的實現及執行順序,以及JS的事件循環的相關問題。

答案:

script startasync1 startasync2promise1script endasync1 endpromise2settimeout

過程詳解鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7

7.將數組扁平化并去除其中重復數據,最終得到一個升序且不重復的數組

Array.from(new Set(arr.flat(Infinity))).sort((a,b)=\u0026gt;{ return a-b})

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/8

8.JS異步解決方案的發展歷程以及優缺點。

1. 回調函數(callback)

setTimeout(() =\u0026gt; {    // callback 函數體}, 1000)

缺點:回調地獄,不能用 try catch 捕獲錯誤,不能 return

回調地獄的根本問題在于:

  • 缺乏順序性: 回調地獄導致的調試困難,和大腦的思維方式不符;

  • 嵌套函數存在耦合性,一旦有所改動,就會牽一發而動全身,即(控制反轉);

  • 嵌套函數過多的多話,很難處理錯誤。

ajax('XXX1', () =\u0026gt; {    // callback 函數體    ajax('XXX2', () =\u0026gt; {        // callback 函數體        ajax('XXX3', () =\u0026gt; {            // callback 函數體        })    })})

優點:解決了同步的問題(只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執行)。

2. Promise

Promise就是為了解決callback的問題而產生的。

Promise 實現了鏈式調用,也就是說每次 then 后返回的都是一個全新 Promise,如果我們在 then 中 return ,return 的結果會被 Promise.resolve() 包裝。

優點:解決了回調地獄的問題

ajax('XXX1')  .then(res =\u0026gt; {      // 操作邏輯      return ajax('XXX2')  }).then(res =\u0026gt; {      // 操作邏輯      return ajax('XXX3')  }).then(res =\u0026gt; {      // 操作邏輯  })

缺點:無法取消 Promise ,錯誤需要通過回調函數來捕獲

3. Generator

特點:可以控制函數的執行,可以配合 co 函數庫使用。

function *fetch() {    yield ajax('XXX1', () =\u0026gt; {})    yield ajax('XXX2', () =\u0026gt; {})    yield ajax('XXX3', () =\u0026gt; {})}let it = fetch()let result1 = it.next()let result2 = it.next()let result3 = it.next()

4. Async/await

async、await 是異步的終極解決方案。

優點是:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調地獄的問題

缺點:await 將異步代碼改造成同步代碼,如果多個異步操作沒有依賴性而使用 await 會導致性能上的降低

async function test() {  // 以下代碼沒有依賴性的話,完全可以使用 Promise.all 的方式  // 如果有依賴性的話,其實就是解決回調地獄的例子了  await fetch('XXX1')  await fetch('XXX2')  await fetch('XXX3')}

下面來看一個使用 await 的例子:

let a = 0let b = async () =\u0026gt; {  a = a + await 10  console.log('2', a) // -\u0026gt; '2' 10}b()a++console.log('1', a) // -\u0026gt; '1' 1

對于以上代碼你可能會有疑惑,讓我來解釋下原因:

  • 首先函數 b 先執行,在執行到 await 10 之前變量 a 還是 0,因為 await 內部實現了 generatorgenerator 會保留堆棧中東西,所以這時候 a = 0 被保存了下來

  • 因為 await 是異步操作,后來的表達式不返回 Promise 的話,就會包裝成 Promise.reslove(返回值),然后會去執行函數外的同步代碼;

  • 同步代碼執行完畢后開始執行異步代碼,將保存下來的值拿出來使用,這時候 a = 0 + 10

上述解釋中提到了 await 內部實現了 generator,其實 await 就是 generator 加上 Promise的語法糖,且內部實現了自動執行 generator。如果你熟悉 co 的話,其實自己就可以實現這樣的語法糖。

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/11

9.談談你對TCP三次握手和四次揮手的理解

\"\"

本題鏈接:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/15

更多內容,請關注前端之巔。

\"\"
會議推薦

2019年6月,GMTC全球大前端技術大會2019即將到來。小程序、Flutter、移動AI、工程化、性能優化…大前端的下一站在哪里?點擊下圖了解更多詳情。

\"\"

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280167.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280167.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280167.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Maven打包小技巧--持續更新

NO.1 跳過測試,打包指定環境 mvn clean install -Dmaven.test.skiptrue -P dev 其中:clean將target目錄中的文件移除; install根據配置文件,將本地工程打包成jar/war包; -Dmaven.test.skiptrue,打包時路過測…

OpenLayers學習筆記5——使用jQuery UI實現查詢并標注(UI篇)

近期事情非常多,老板給的壓力也非常大。經常出差,另外項目和個人研究還都要跟上,本月要交論文,還要寫專利,僅僅能抽時間來學習其它的東西了。 關于OpenLayers的在博客中不會寫太多詳細的實現(網上有非常多o…

C++ 排序函數 sort(),qsort()的用法

想起來自己天天排序排序,冒泡啊,二分查找啊,結果在STL中就自帶了排序函數sort,qsort,總算把自己解脫了~ 所以自己總結了一下,首先看sort函數見下表: 函數名功能描述sort對給定區間所有元素進行排序stable_s…

.net core 實現默認圖片

web 上 如果圖片不存在 一般是打xx 這時候 一般都是會設置默認的圖片 代替 現在用中間件的方式實現統一設置 一次設置 全部作用 .net core 實現默認圖片 Startup 文件 app.UseDefaultImage(defaultImagePath: Configuration.GetSection("defaultImagePath").Va…

spring cloud config將配置存儲在數據庫中

轉載請標明出處: https://blog.csdn.net/forezp/...本文出自方志朋的博客 Spring Cloud Config Server最常見是將配置文件放在本地或者遠程Git倉庫,放在本地是將將所有的配置文件統一寫在Config Server工程目錄下,如果需要修改配置&#xff0…

VMware虛擬機VMware Authorization Service不能啟動問題

出現VMware Authorization Service不能啟動問題,注意要在安裝VMware Player時使用管理員權限轉載于:https://www.cnblogs.com/mingzhang/p/9152873.html

PHP替換回車換行的三種方法

一個小小的換行,其實在不同的平臺有著不同的實現,為什么要這樣,世界是多樣的!本來在Unix世界換行用/n來代替換行,Windows為了體現不同,就用/r/n,更有意思的是,Mac中又用了/r。所以&a…

全球的weex資源都在這里

WeeX FAQ QQ: Weex大前端 516682889Weexbox: 943913583WeeX相關資源 weex官方資源 weex官網 Weex Market 已掛 : 一個提供 Weex 第三方組件的網站,您可以在這里找到你需要的 Weex 組件。 Playground : Playground在線,直接在線編寫代碼并預覽…

初步解決博客園代碼高亮的一個方案

今天我要推薦的是一個免費而且支持markdown語法的軟件——Typora 它有很多優點,支持多種類型代碼的高亮風格,方便的排版處理,支持Latex等,最重要的一點是真正做到了所見即所得ヽ(゚?゚)メ(&#x…

git工作原理

工作區:就是你在電腦里能看到的目錄。暫存區:英文叫stage, 或index。一般存放在 ".git目錄下" 下的index文件(.git/index)中,所以我們把暫存區有時也叫作索引(index)。版本庫&#xf…

【前端基礎進階】JS-Object 功能詳解

Object.assign(target,source1,source2,...)該方法主要用于對象的合并,將源對象source的所有可枚舉屬性合并到目標對象target上,此方法只拷貝源對象的自身屬性,不拷貝繼承的屬性。Object.assign方法實行的是淺拷貝,而不是深拷貝。也就是說&am…

解決“無法從套接字讀取更多數據”

重啟下Oralce服務即可。轉載于:https://www.cnblogs.com/fkeyta/p/9153297.html

網頁下載Google Play 的App

網頁下載Google Play 的App 文章目錄[點擊展開](?)[] 前言 當你想在google play上下載某個應用,而無奈手機的系統并沒有安裝google servicess,此刻是否有些捉急? 本文分享的是一個網站,它可以無需手機而直接通過網頁下載Google P…

“硬核”代碼重構

在學習編程的路上,相信大家這幾個詞一定不少聽,什么 面相對象、封裝繼承多態、內功心法21種設計模式 等等 。但是卻很少用到,或者說用到的都是被動使用。大牛們在寫代碼前早就構思好了,接口,基類等等。自己寫代碼的時候…

上傳jar包到nexus私服

進入maven管理頁面&#xff0c;登錄管理員賬號 完成后可以進入對應目錄下查看pom依賴 通過maven的方式depoly 在maven的conf/setting.xml 配置nexus私服的管理賬號 在servers標簽下添加server <server><id>nexus-snapshots</id><username>repouser<…

手把手教你寫高質量Android技術博客,畫圖工具,錄像工具,Markdown寫法

前言 作為程序員&#xff0c;寫博客是一件很有意義的事情&#xff0c;可以加深自己對技術的理解&#xff0c;可以結交更多的朋友&#xff0c;記錄自己的技術軌跡&#xff0c;而且分享可以讓更多的人從中受益&#xff0c;獨樂樂不如眾樂樂嘛。 但是要寫好博客也不是件容易的事&a…

【Android】RxJava的使用(四)線程控制 —— Scheduler

前言 經過前幾篇的介紹&#xff0c;對RxJava對模式有了一定的理解&#xff1a;由Observable發起事件&#xff0c;經過中間的處理后由Observer消費。&#xff08;對RxJava還不了解的可以出門左拐&#xff09;之前的代碼中&#xff0c;事件的發起和消費都是在同一個線程中執行&am…

sed: -e expression #1, unknown option to `s'解決辦法

報錯如下&#xff1a; sed: -e expression #1, char 13: unknown option to s 需要替換的行為&#xff1a; monitor.urlhttp://192.168.25.100:8443/rest 查詢資料得知&#xff0c;報錯是因為替換的字符串包含有分隔符/ 所以這行改一下分隔符就可以解決問題了 改成感嘆號!或者|…

Linux常用開發環境軟件-Redis安裝(docker環境下)

linux&#xff0c;docker安裝RabbitMQ版本 1、從docker官網倉庫下載安裝RabbitMQ鏡像 官網地址&#xff1a;https://hub.docker.com/ docker pull redis:4.0.8  //后面是版本,Tag Name 2、啟動Docker Redis鏡像 docker run -d -p 6379:6379 redis:4.0.8  啟動鏡像&#xff…

以當天日期時間,打包目錄

#備份/data目錄#!/bin/bash DATEdate %Y-%m-%d-%H:%M:%S tar cvf /mnt/resource/script/prod_master_data.$DATE.tar.gz /data[root111 script]# ll total 2536 -rw-r--r-- 1 root root 2590720 Feb 22 21:46 prod_master_data.2019-02-22-21:46:53.tar.gz轉載于:https://blog.…