<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          最近一個(gè)月把大廠面了個(gè)遍,還未上岸……

          共 28833字,需瀏覽 58分鐘

           ·

          2021-09-30 09:49

          本文由我的讀者小伙伴 sensFeng 投稿,這一個(gè)月他面了很多家大公司,這份面試經(jīng)驗(yàn)對(duì)最近正在準(zhǔn)備找工作的小伙伴可以說(shuō)是非常有參考價(jià)值了,在文章中他也給出了他整理的答案,誠(chéng)意滿滿!

          也可以前往掘金查看:https://juejin.cn/post/6995744994166308895

          前言

          筆者兩年前端經(jīng)驗(yàn),前后大概面了一個(gè)月,期間面了很多公司,比如有贊、涂鴉智能滴滴、字節(jié)、酷家樂(lè) 大搜車(chē)??低?/code>、稅友等等,梳理一下基于我個(gè)人面試過(guò)程中被問(wèn)的到的一些問(wèn)題(包括但不限于)

          在開(kāi)始面試之前,一份優(yōu)秀的簡(jiǎn)歷也是十分重要,推薦兩篇文章:

          為了讓自己拿到比較滿意的 offer,前一周選了一些中小公司來(lái)練手,后面發(fā)現(xiàn),太小的公司沒(méi)有鍛煉效果,所以一周后,就開(kāi)始給規(guī)模比較大的廠投簡(jiǎn)歷了。

          印象比較深刻的是酷家樂(lè),一面是遠(yuǎn)程,然后二面約你現(xiàn)場(chǎng),現(xiàn)場(chǎng)是三輪技術(shù) + hr,差不多三個(gè)小時(shí),效率很高,但是第一次面試這么久(當(dāng)時(shí)也不知道要面這么久),導(dǎo)致后面表現(xiàn)的不是很好,遂,卒。涂鴉智能的效率也很高,跟酷家樂(lè)的面試流程是一樣的,都是遠(yuǎn)程一面,現(xiàn)場(chǎng)三輪技術(shù) + hr。其他的公司基本上是一輪一輪的來(lái),每一輪的結(jié)果一般第二天會(huì)告知你,當(dāng)然,如果后面幾天等不到,也是一種告知。

          悄悄加一句,歡迎聯(lián)系 ssh 內(nèi)推或者交個(gè)朋友 ??,微信 sshsunlight

          HTTP

          http 的問(wèn)題算是面試熱點(diǎn)問(wèn)題,在一些交叉面或者一面里面很喜歡問(wèn),是比較考驗(yàn)?zāi)銓?duì)基礎(chǔ)的掌握。

          get 和 post 有什么區(qū)別?

          請(qǐng)求參數(shù):get 請(qǐng)求參數(shù)是通過(guò) url 傳遞的,多個(gè)參數(shù)以&連接;POST 請(qǐng)求放在 request body 中。
          請(qǐng)求緩存:get 請(qǐng)求會(huì)被緩存,而 post 請(qǐng)求不會(huì),除非手動(dòng)設(shè)置。
          相對(duì)的安全性:get 是將參數(shù)通過(guò) url 傳遞的,會(huì)被瀏覽器緩存,容易被他人獲取,post 相對(duì)來(lái)說(shuō),比較安全。
          請(qǐng)求參數(shù)長(zhǎng)度限制:get 通過(guò) url 傳參,瀏覽器會(huì)限制 url 的長(zhǎng)度(http不會(huì))。
          編碼方式:GET 請(qǐng)求只能進(jìn)行 url 編碼,而 POST 支持多種編碼方式。

          http1.1 和 http2.0 有什么區(qū)別?

          (包括但不限于 1.1 和 2.0 的版本對(duì)比)

          http1.1

          • 引入了持久鏈接,即 TCP 默認(rèn)不關(guān)閉,可以被多個(gè)請(qǐng)求復(fù)用
          • 引入管道機(jī)制,一個(gè) TCP 連接,可以同時(shí)發(fā)送多個(gè)請(qǐng)求
          • 新增了一些緩存的字段
          • 新增了一些方法,PUT、DELETE、OPTIONS、PATCH
          • 支持?jǐn)帱c(diǎn)續(xù)傳,通過(guò)請(qǐng)求頭字段 Rang 來(lái)實(shí)現(xiàn)

          http2.0

          • 頭部壓縮
          • 多路復(fù)用
          • 二進(jìn)制傳輸,頭信息和數(shù)據(jù)體都是二進(jìn)制
          • 請(qǐng)求優(yōu)先級(jí), 設(shè)置數(shù)據(jù)幀的優(yōu)先級(jí),讓服務(wù)器優(yōu)先處理
          • 服務(wù)器主動(dòng)推送消息

          被問(wèn)到的一些問(wèn)題:

          • 管道機(jī)制會(huì)造成什么樣的問(wèn)題,http2.0 是怎么解決的
          • 頭部壓縮的原理是什么
          • options 方法的作用
          • http2.0 允許服務(wù)器主動(dòng)推送消息,那跟 WebSocket 有什么區(qū)別嗎?

          推薦一篇神三元大佬的文章:HTTP 靈魂之問(wèn),鞏固你的 HTTP 知識(shí)體系[2]

          說(shuō)一下 http 緩存

          等你說(shuō)完強(qiáng)緩存協(xié)商緩存的大致流程,面試官可能基于你的答案,來(lái)深入考察你對(duì) http 緩存的理解,比如(包括但不限于):

          200 狀態(tài)碼一定是服務(wù)器返回的嗎?

          不是,命中強(qiáng)緩存的話,會(huì)直接從內(nèi)存或者磁盤(pán)中讀取資源,并返回一個(gè) 200 狀態(tài)碼,具體操作可以試試瀏覽器的前進(jìn)后退鍵。

          Expires 和 Cache-Control 的 max-age 指令分別是如何確定過(guò)期時(shí)間的??jī)?yōu)劣勢(shì)是什么?

          為什么有了 Last-Modified 還要有 ETag?解決了什么問(wèn)題?

          no-store 和 no-cache 的意思分別是什么?

          推薦一篇文章,解答了這些問(wèn)題:一張圖理解 http 緩存[3]

          https 為什么比 http 安全

          面試官會(huì)讓你說(shuō)下 https 做了什么,然后不排除會(huì)問(wèn)你加密原理。(ps: 字節(jié)面試官問(wèn)了)

          主要用到了對(duì)稱加密和非對(duì)稱加密,推薦閱讀:徹底搞懂 HTTPS 的加密原理[4]

          說(shuō)一下三次握手四次揮手

          這個(gè)問(wèn)題問(wèn)的比較少,只遇到過(guò)一次。推薦一篇筆者之前寫(xiě)的文章: TCP 三次握手、四次揮手[5]

          JS

          0.1 + 0.2 !== 0.3?為什么?

          • 二進(jìn)制轉(zhuǎn)換:js 在做數(shù)字計(jì)算的時(shí)候,底層都是轉(zhuǎn)二進(jìn)制來(lái)計(jì)算的,0.1 轉(zhuǎn)二進(jìn)制是無(wú)限循環(huán)小數(shù),0.2 也是,但是 js 采用的IEEE754 二進(jìn)制浮點(diǎn)運(yùn)算[6],小數(shù)后面只會(huì)保留 53 位有效數(shù)字,導(dǎo)致精度丟失。
          • 對(duì)階運(yùn)算:階小的尾數(shù)要根據(jù)階差來(lái)右移,尾數(shù)位移時(shí)可能會(huì)發(fā)生數(shù)丟失的情況,影響精度。

          如何解決上面說(shuō)的精確度丟失問(wèn)題?

          • 剛開(kāi)始第一反應(yīng)就是,先將小數(shù)點(diǎn)擴(kuò)大變成整數(shù),做完加法之后,再除回去。(不妥當(dāng),因?yàn)檫€是可能會(huì)超過(guò) js 最大數(shù))
          • 利用第三方庫(kù),比如Math.js、big.js
          • 都轉(zhuǎn)成字符串,然后對(duì)兩個(gè)字符串做加法運(yùn)算(手寫(xiě)題有實(shí)現(xiàn))

          閉包

          • 說(shuō)一下閉包的本質(zhì)是什么?會(huì)造成哪些問(wèn)題
          • 除了函數(shù)的內(nèi)部返回一個(gè)函數(shù),還有其他的方法產(chǎn)生閉包嗎?(有)
          // 函數(shù)內(nèi)部延遲調(diào)用,產(chǎn)生了閉包
          function test({
            let name = "tom";
            setTimeout(() => {
              console.log(name);
            }, 2000);
          }
          test();

          推薦文章:JavaScript 的靜態(tài)作用域鏈與“動(dòng)態(tài)”閉包鏈[7]

          什么是作用域?什么是作用域鏈?函數(shù)執(zhí)行上下文包含了哪些內(nèi)容?

          this 指向

          面試官問(wèn):JS 的 this 指向[8]

          es6 的問(wèn)題

          • 箭頭函數(shù)和普通函數(shù)的區(qū)別
          • 什么是暫時(shí)性死區(qū),什么是變量提升
          • for of 和 for in 的區(qū)別,怎么讓 for of 可以遍歷一個(gè)對(duì)象?
          • es6 的 Map 和 WeakMap 的區(qū)別,WeakMap 解決了什么問(wèn)題?
          • promise 哪些方法是原型上的,哪些方法是實(shí)例上的

          推薦阮一峰老師的教程:ES6 入門(mén)教程[9]

          原型 + 原型鏈 (這個(gè)屬于必問(wèn)的題)

          這個(gè)問(wèn)題只要能描述的清楚對(duì)象如何查找它的值,基本上就理解了一大半。

          解釋一下上面的圖:

          • prototype是函數(shù)特有的屬性,__proto__是每個(gè)對(duì)象都有的屬性,而prototype本身也是一個(gè)對(duì)象
          • 當(dāng)我們?nèi)カ@取a.name的時(shí)候,會(huì)先從對(duì)象的自身屬性開(kāi)始查找,如果沒(méi)有的話,就會(huì)從a.__proto__上找
          • 對(duì)象a.__proto__又指向構(gòu)造器函數(shù)testprototype(原型),所以從a.__proto上找屬性其實(shí)就是在test.prototype找屬性,但是prototype(原型)本身又是一個(gè)對(duì)象,這樣的話,會(huì)重復(fù)上面兩個(gè)步驟,最終都是找到了Object這個(gè)構(gòu)造器函數(shù),而Object.__proto是一個(gè) null 值,如果中間有值就返回,沒(méi)有就賦值undefined。
          • 這樣的鏈?zhǔn)浇Y(jié)構(gòu)就是原型鏈

          因?yàn)闃?gòu)造器函數(shù)原型上的constructor是指向構(gòu)造器函數(shù)自身的,所以

          a.constructor === test; // true
          a.__proto__.constructor === test; // true
          a.__proto__.constructor === test.prototype.constructor; // true

          有一道面試題可以測(cè)試一下,說(shuō)出打印內(nèi)容,并且說(shuō)明原因。

          function test({}
          test.prototype.then = function ({
            console.log("test => then");
          };
          Function.prototype.mythen = function ({
            console.log("Function => mythen");
          };
          test.mythen();
          test.then();

          eventLoop

          面試官會(huì)基于你的回答來(lái)提問(wèn),比如:

          • 你剛剛說(shuō)到 js 是單線程,那線程跟進(jìn)程有什么區(qū)別?
          • 瀏覽器新開(kāi)了一個(gè)頁(yè)面,有幾個(gè)線程?
          • 為什么要設(shè)計(jì)成微任務(wù)先執(zhí)行,宏任務(wù)后執(zhí)行。

          推薦一篇酷家樂(lè)大佬的文章:這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制[10]

          垃圾回收機(jī)制

          • 你剛剛提到的標(biāo)記清除法有什么缺點(diǎn)?怎么解決?
          • 你剛剛提到的引用計(jì)數(shù)法有什么缺點(diǎn)嗎?
          • v8 里面的垃圾回收機(jī)制是什么?
          • v8 是怎么解決循環(huán)引用的?

          推薦文章:你真的了解垃圾回收機(jī)制嗎[11]

          說(shuō)一下數(shù)據(jù)類(lèi)型,如何判斷一個(gè)數(shù)組

          判斷數(shù)組的方法:

          • Array.isArray(arr); // true
          • arr instanceof Array; // true
          • arr.constructor === Array; // true
          • Object.prototype.toString.call(arr); // "[object Array]"

          ps:通過(guò) instanceof 和 constructor 來(lái)判斷不一定準(zhǔn)確,因?yàn)榭赡軙?huì)被重寫(xiě)。

          常用的設(shè)計(jì)模式?

          • 什么是抽象工廠模式
          • 發(fā)布訂閱模式和觀察者模式有什么區(qū)別
          • 你項(xiàng)目里面都用了哪些設(shè)計(jì)模式

          推薦文章:前端需要掌握的設(shè)計(jì)模式[12]

          瀏覽器渲染過(guò)程

          一般我都是根據(jù)這張圖,把流程說(shuō)一遍。

          被面試官問(wèn)到的一些問(wèn)題:

          • link 標(biāo)簽會(huì)不會(huì)阻塞頁(yè)面的渲染?說(shuō)一下原因?
          • 為什么 css 推薦放上面,js 推薦放下面?
          • js 會(huì)阻塞頁(yè)面的渲染嗎?說(shuō)一下原因?
          • css 會(huì)阻塞 html 的解析嗎?為什么?
          • css 會(huì)阻塞 html 的渲染嗎?為什么?

          js 執(zhí)行是單獨(dú)的線程,瀏覽器渲染是GUI渲染線程負(fù)責(zé)的,兩個(gè)線程是互斥關(guān)系,所以很好理解,js 會(huì)阻塞頁(yè)面的渲染。

          css 不會(huì)阻塞 html 的解析,解析 html 和解析 css 是并行的,但是 css 會(huì)阻塞 html 的渲染,因?yàn)轫?yè)面渲染的時(shí)候,需要style Rules + DOM Tree一起合成Render Tree

          正常來(lái)說(shuō),解析頁(yè)面過(guò)程中如果遇到一個(gè) script 標(biāo)簽,會(huì)停止 html 解析(如下圖),去下載 script 腳本,下載完畢之后立即執(zhí)行腳本,然后接著解析 html,所以如果 script 下載速度很慢,會(huì)造成頁(yè)面白屏。

          defer:html 解析和腳本下載并行(異步),等 html 解析之后,DOMContentLoaded觸發(fā)之前執(zhí)行腳本。
          async:html 解析和腳本下載并行(異步),下載后立即執(zhí)行腳本,且中斷 html 解析。

          推薦閱讀:
          瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘[13]、

          從 8 道面試題看瀏覽器渲染過(guò)程與性能優(yōu)化[14]

          性能優(yōu)化

          我一般從 http 請(qǐng)求 + 代碼層面 + 構(gòu)建工具來(lái)回答。

          setTimeout 和 requestAnimationFrame 做動(dòng)畫(huà)有區(qū)別嗎?哪一個(gè)更好?為什么?

          有區(qū)別:

          • 準(zhǔn)確性

          setTimeout做動(dòng)畫(huà)不準(zhǔn)確,因?yàn)槭呛耆蝿?wù),設(shè)置的延遲時(shí)間并不等于在延遲時(shí)間之后立即執(zhí)行,因?yàn)樾枰却饺蝿?wù)和微任務(wù)執(zhí)行完,才執(zhí)行宏任務(wù),容易丟幀;requestAnimationFrame則是在每一幀繪制元素之前執(zhí)行,更精確。

          • 更好的性能

          在隱藏元素或者元素不可見(jiàn)時(shí),setTimeout仍然在后臺(tái)執(zhí)行動(dòng)畫(huà)任務(wù);而requestAnimationFrame會(huì)停止動(dòng)畫(huà),這意味著更少的 CPU 和更少的內(nèi)存消耗。

          介紹一下同源策略?你知道那些跨域方法?cors 跨域的原理是什么有了解過(guò)嗎?

          CSS

          介紹一下盒模型?

          flex: 1 代表什么意思

          用過(guò) flex 布局嗎?都有哪些屬性?

          說(shuō)說(shuō)什么是 BFC,一般你都用來(lái)干什么,解決了什么問(wèn)題?

          實(shí)現(xiàn)元素水平垂直居中?盡可能說(shuō)多一些方法?

          左側(cè)固定 + 右側(cè)自適應(yīng)布局?說(shuō)說(shuō)幾種方案?

          重繪和重排?

          推薦文章:重排(reflow)和重繪(repaint)[15]

          React

          面了一圈下來(lái),發(fā)現(xiàn) react 問(wèn)的都差不多。

          都用過(guò)那些版本的 react,分別介紹一下區(qū)別?

          說(shuō)一下前端路由,他們的實(shí)現(xiàn)原理分別是什么?

          hash 路由:通過(guò)監(jiān)聽(tīng)hashchange事件來(lái)捕捉 url 的變化,來(lái)決定是否更新頁(yè)面。

          history 路由:主要監(jiān)聽(tīng)popState、pushStatereplaceState來(lái)決定是否更新頁(yè)面。但是要注意,僅僅調(diào)用pushState方法或replaceState方法 ,并不會(huì)觸發(fā)popState事件,只有用戶點(diǎn)擊瀏覽器倒退按鈕和前進(jìn)按鈕,或者使用 JavaScript 調(diào)用backforward、go方法時(shí)才會(huì)觸發(fā),想要pushState、replaceState的時(shí)候觸發(fā)popState事件,需要自定義事件。

          你能手寫(xiě)個(gè)簡(jiǎn)單的 redux 嗎?

          面試官說(shuō)完之后,給我遞過(guò)來(lái)筆和紙......(內(nèi)心崩潰) 寫(xiě)完了之后,面試官讓我給他講一遍。

          function createStore(reducer{
            let listeners = [];
            let currentState;
            function getState({
              return currentState;
            }
            function dispatch(action{
              currentState = reducer(currentState, action);
              listeners.forEach((l) => l());
            }
            function subscribe(fn{
              listeners.push(fn);
              return function unsubscribe({
                listeners = listeners.filter((l) => l !== fn);
              };
            }
            return {
              getState,
              dispatch,
              subscribe,
            };
          }

          redux 里面 dispatch 是如何正確找到 reducer 的?

          combineReducers 源碼[16]

          是的,redux 它不找,一把梭,每次 dispatch 都要遍歷一遍所有的 reducer...

          redux 怎么掛載中間件的?它的執(zhí)行順序是什么樣的?

          核心函數(shù)compose

          function compose(middlewears{
            return middlewears.reduce(
              (a, b) =>
                (...argu) =>
                  a(b(...argu))
            );
          }

          執(zhí)行順序:從后往前執(zhí)行 redux 中間件。

          除了 redux,還用過(guò)其他的狀態(tài)管理庫(kù)嗎?

          沒(méi)有。

          redux 的缺點(diǎn)?

          react 生命周期?

          setState 什么情況下同步,什么情況下異步?

          講一下 react 的事件機(jī)制,為什么這么設(shè)計(jì)?react17 里面有什么變化嗎?

          推薦文章:一文吃透 react 事件系統(tǒng)原理[17]

          設(shè)計(jì)的好處

          • 抹平瀏覽器差異,實(shí)現(xiàn)更好的跨平臺(tái)
          • 避免垃圾回收,React 引入事件池,在事件池中獲取或釋放事件對(duì)象,避免頻繁地去創(chuàng)建和銷(xiāo)毀
          • 方便事件統(tǒng)一管理和事務(wù)機(jī)制

          class 組件跟函數(shù)組件有什么區(qū)別?

          能在 if 判斷里面寫(xiě) hooks 嗎?為什么不能?

          Hooks 是用鏈表保存狀態(tài)的,每次渲染的時(shí)候,必須要保證 hooks 的長(zhǎng)度和順序是一樣的,如果不一致,react 無(wú)法獲取正確狀態(tài),會(huì)報(bào)錯(cuò)。

          HOC 和 hooks 的區(qū)別?

          hooks 實(shí)現(xiàn)原理?不用鏈表可以用其他方法實(shí)現(xiàn)嗎?

          基于鏈表來(lái)實(shí)現(xiàn)的,也可以用數(shù)組來(lái)模擬。

          useEffect 依賴傳空數(shù)組和 componentDidMount 有什么區(qū)別嗎?

          useeffect 和 useLayouteffect 區(qū)別

          • useEffect不會(huì)阻塞瀏覽器渲染,而  useLayoutEffect  會(huì)阻塞瀏覽器渲染。
          • useEffect會(huì)在瀏覽器渲染結(jié)束后執(zhí)行,useLayoutEffect  則是在  DOM  更新完成后,瀏覽器繪制之前執(zhí)行。

          介紹一下 react dom diff

          介紹一下 Vdom?

          在 React 中,有做過(guò)什么性能優(yōu)化嗎?

          按照自己腦袋瓜里面的想法,說(shuō)了幾種。

          推薦文章:React 性能優(yōu)化的 8 種方式了解一下[18]

          React.memo()和 React.useMemo()有什么區(qū)別嗎?

          接上題,然后面試官這兩個(gè)有什么區(qū)別嗎?

          直接上官網(wǎng): React.memo[19]、 React.useMemo[20]

          useCallback 和 useMemo 的區(qū)別?

          同接上題 官網(wǎng)地址 useCallback[21]

          React.fiber 了解嗎?造成卡頓的原因是什么?react.fiber 里面是怎么解決的?中斷更新之后,下次是如何找到要更新的位置的?

          推薦荒山大佬的文章:這可能是最通俗的 React Fiber(時(shí)間分片) 打開(kāi)方式[22]

          函數(shù)式編程

          講 hooks 很大概率問(wèn)到函數(shù)式編程?(被問(wèn)到過(guò)好幾次)

          • 說(shuō)一下你理解的函數(shù)式編程?
          • 有哪些庫(kù)是利用了函數(shù)式編程的思想?
          • lodash 是真正的函數(shù)式編程庫(kù)嗎?

          簡(jiǎn)明 JavaScript 函數(shù)式編程——入門(mén)篇[23]

          高階組件里面如何防止 ref 丟失?

          React.forwardRef,上官網(wǎng)地址:React.forwardRef[24]

          webpack && node

          • 都用過(guò) node 干了什么?用過(guò) node 框架嗎?
          • node 一些基本 api,如何刪除文件,創(chuàng)建文件,寫(xiě)入文件。
          • 用過(guò) node 的哪些模塊?

          進(jìn)程和線程的區(qū)別

          介紹一下模塊發(fā)展史

          node_modules 問(wèn)題

          假如 npm 安裝了一個(gè)模塊 A、依賴 c 的 0.0.1 版本,又安裝了一個(gè)模塊 B,依賴 c 的 0.0.2 版本。請(qǐng)問(wèn) node_module 是怎么保證 A、B 正確的找到對(duì)應(yīng)的 c 版本的包的?

          webpack 打包原理

          • 初始化參數(shù):從配置文件和 Shell 語(yǔ)句中讀取與合并參數(shù),得出最終的參數(shù);
          • 開(kāi)始編譯:用上一步得到的參數(shù)初始化 Compiler 對(duì)象,加載所有配置的插件,執(zhí)行對(duì)象的 run 方法開(kāi)始執(zhí)行編譯;確定入口:根據(jù)配置中的 entry 找出所有的入口文件
          • 編譯模塊:從入口文件出發(fā),調(diào)用所有配置的 Loader 對(duì)模塊進(jìn)行編譯,再找出該模塊依賴的模塊,再遞歸本步驟直到所有入口依賴的文件都經(jīng)過(guò)了本步驟的處理;
          • 完成模塊編譯:在經(jīng)過(guò)第 4 步使用 Loader 翻譯完所有模塊后,得到了每個(gè)模塊被翻譯后的最終內(nèi)容以及它們之間的依賴關(guān)系 輸出資源:根據(jù)入口和模塊之間的依賴關(guān)系,組裝成一個(gè)個(gè)包含多個(gè)模塊的 Chunk,再把每個(gè) Chunk 轉(zhuǎn)換成一個(gè)單獨(dú)的文件加入到輸出列表,這步是可以修改輸出內(nèi)容的最后機(jī)會(huì)
          • 輸出完成:在確定好輸出內(nèi)容后,根據(jù)配置確定輸出的路徑和文件名,把文件內(nèi)容寫(xiě)入到文件系統(tǒng)。

          webpack 性能優(yōu)化你是怎么做的?

          推薦文章:帶你深度解鎖 Webpack 系列(優(yōu)化篇)[25]

          loader 和 plugin 的區(qū)別?

          • loader(轉(zhuǎn)換):主要是做轉(zhuǎn)換功能,比如將 css、less 文件轉(zhuǎn)成 js,識(shí)別不同的文件后綴名,可以拓展一下自己比較熟悉的 loader。
          • plugin(插件):原理是監(jiān)聽(tīng) webpack 構(gòu)建過(guò)程中的一些鉤子,然后做一些自己的操作,更多的是豐富 webpack 的功能。

          loader 的執(zhí)行順序是什么?如何寫(xiě)一個(gè) loader?如何寫(xiě)一個(gè) plugin?loader 有異步的嗎?

          執(zhí)行順序從右往左、從下到上。

          loader 本質(zhì)是一個(gè)函數(shù),plugin 本質(zhì)是一個(gè)類(lèi),具體如何編寫(xiě),推薦文章:手把手教你寫(xiě)一個(gè) loader / plugin[26]

          loader 有同步的也有異步。

          file-loader 返回的是什么?

          返回的是一個(gè)字符串,詳情見(jiàn):file-loader 源碼地址[27]

          webpack 有幾種 hash,它們有什么區(qū)別?一般你在項(xiàng)目里面是用哪種 hash?

          • hash:是整個(gè)項(xiàng)目的 hash 值,其根據(jù)每次編譯內(nèi)容計(jì)算得到,每次編譯之后都會(huì)生成新的 hash,即修改任何文件都會(huì)導(dǎo)致所有文件的 hash 發(fā)生改變。
          • chunkHash:根據(jù)不同的入口文件(Entry)進(jìn)行依賴文件解析、構(gòu)建對(duì)應(yīng)的 chunk,生成對(duì)應(yīng)的哈希值(來(lái)源于同一個(gè) chunk,則 hash 值就一樣)。
          • contentHash:根據(jù)文件內(nèi)容生成 hash 值,文件內(nèi)容相同 hash 值就相同

          webpack5 有哪些新特性?

          推薦文章:飛書(shū)團(tuán)隊(duì) Webpack5 上手測(cè)評(píng)[28]

          webpack 熱更新原理?

          推薦文章:輕松理解 webpack 熱更新原理[29]

          tree-shaking 原理?

          利用ES Module做靜態(tài)分析,通過(guò)分析 ast 語(yǔ)法樹(shù),對(duì)每個(gè)模塊維護(hù)了一個(gè)作用域,收集模塊內(nèi)部使用的變量,然后分析作用域,將import進(jìn)來(lái)未被使用的模塊刪除,最后遞歸處理文件。

          babel 轉(zhuǎn)換代碼的過(guò)程,箭頭函數(shù)轉(zhuǎn)普通函數(shù)的時(shí)候,是如何處理 this 的?

          過(guò)程:parser => transfrom => generator,可以根據(jù)自己的理解,展開(kāi)說(shuō)說(shuō)。

          箭頭函數(shù)轉(zhuǎn)普通函數(shù)如何處理 this:就近找上一層作用域里面的 this,用一個(gè)唯一變量名 that 緩存一下 this,然后將之前箭頭函數(shù)里面的 this 替換成 that 即可。

          手寫(xiě)題

          節(jié)流和防抖

          (某獨(dú)角獸公司)面試官:你能手寫(xiě)個(gè)節(jié)流函數(shù)嗎?然后遞過(guò)來(lái)了筆跟紙......

          函數(shù)防抖

          定義:在 n 秒時(shí)間內(nèi),函數(shù)只會(huì)觸發(fā)一次,如果期間被觸發(fā),則重新計(jì)時(shí)。

          場(chǎng)景input框?qū)崟r(shí)搜索、瀏覽器的resize、和scroll

          function debounce(fn, delay{
            let timer;
            return function (...argu{
              let that = this;
              if (timer) clearTimeout(timer);
              timer = setTimeout(() => {
                fn.apply(that, argu);
              }, delay);
            };
          }

          函數(shù)節(jié)流

          定義:在 n 秒內(nèi),事件只執(zhí)行一次,如果期間被觸發(fā),也不會(huì)響應(yīng)事件。

          場(chǎng)景表單重復(fù)提交、滾動(dòng)加載

          // 利用時(shí)間戳實(shí)現(xiàn)
          function throttle(fn, delay{
            let previous = new Date();
            return function (...argu{
              let nowDate = new Date();
              if (nowDate - previous > delay) {
                fn.apply(this, argu);
                previous = nowDate;
              }
            };
          }

          // 利用定時(shí)器實(shí)現(xiàn)
          function throttle(fn, delay{
            let timer;
            return function (...argu{
              let that = this;
              if (timer) return false;
              timer = setTimeout(() => {
                fn.apply(that, argu);
                timer = null// 釋放timer變量,讓下一次的函數(shù)接著執(zhí)行
              }, delay);
            };
          }

          寫(xiě)一種你熟悉的排序?

          沒(méi)錯(cuò),也是用筆寫(xiě),寫(xiě)完了然后給他講思路。我選了一個(gè)快速排序

          // 先選一個(gè)數(shù)當(dāng)作基點(diǎn),一般選擇最后一個(gè)數(shù)
          // 然后遍歷arr, 找出這個(gè)基點(diǎn)數(shù)的比它大的數(shù)組集合和比它小的數(shù)組集合
          // 遞歸此步驟
          function quickSort(arr{
            if (arr.length < 2) {
              return arr;
            }
            const cur = arr[arr.length - 1];
            let left = [];
            let right = [];
            for (let i = 0; i < arr.length - 1; i++) {
              if (arr[i] >= cur) {
                right.push(arr[i]);
              } else {
                left.push(arr[i]);
              }
            }
            return [...quickSort(left), cur, ...quickSort(right)];
          }
          console.log(quickSort([1336241]));

          (字節(jié))說(shuō)出打印順序

          默認(rèn)非嚴(yán)格模式

          function Foo({
            getName = function ({
              alert(1);
            };
            return this;
          }
          Foo.getName = function ({
            alert(2);
          };
          Foo.prototype.getName = function ({
            alert(3);
          };
          var getName = function ({
            alert(4);
          };
          function getName({
            alert(5);
          }

          // 請(qǐng)寫(xiě)出以下輸出結(jié)果:
          Foo.getName();
          getName();
          Foo().getName();
          getName();
          new Foo.getName();
          new Foo().getName();

          正確輸出順序:2 4 1 1 2 3

          • Foo.getName();這個(gè)沒(méi)什么好說(shuō)的,輸出 2

          • getName();考察 var 和函數(shù)提升,函數(shù)優(yōu)先級(jí)大于 var,所以輸出 4

          • Foo().getName();Foo()返回 this,此時(shí) this 指向 window,F(xiàn)oo().getName 相當(dāng)于 window.getName。但是 Foo()內(nèi)部又對(duì) window 上的 getName 重新賦值了,所以輸出 1

          • getName();同上,輸出 1

          • new Foo.getName();考察運(yùn)算符優(yōu)先級(jí)[30],new 無(wú)參數(shù)列表,對(duì)應(yīng)的優(yōu)先級(jí)是 18;成員訪問(wèn)操作符 . , 對(duì)應(yīng)的優(yōu)先級(jí)是 19。因此相當(dāng)于是 new (Foo.getName)();new 操作符會(huì)執(zhí)行構(gòu)造函數(shù)中的方法,因此此處輸出為 2.

          • new Foo().getName();new 帶參數(shù)列表,對(duì)應(yīng)的優(yōu)先級(jí)是 19,和成員訪問(wèn)操作符.優(yōu)先級(jí)相同。同級(jí)運(yùn)算符,按照從左到右的順序依次計(jì)算。new Foo()先初始化 Foo 的實(shí)例化對(duì)象,實(shí)例上沒(méi)有 getName 方法,因此需要原型上去找,即找到了 Foo.prototype.getName,輸出 3

          instanceof 原理,能?chē)L試手寫(xiě)一個(gè) instanceof 函數(shù)嗎

          function myInstanceOf(left, right{
            if (left === null || typeof left !== "object"return false;
            if (typeof right !== "function"throw new Error("right must be function");
            let L = left.__proto__;
            let R = right.prototype;
            while (L !== null) {
              if (L === R) return true;
              L = L.__proto__;
            }
            return false;
          }

          實(shí)現(xiàn) new 關(guān)鍵字

          • 創(chuàng)建一個(gè)空的簡(jiǎn)單 javaScript 對(duì)象,即{}
          • 將創(chuàng)建對(duì)象的__proto__指向構(gòu)造函數(shù)的prototype
          • 修改this指向
          • 如果構(gòu)造函數(shù)沒(méi)有返回值,就返回創(chuàng)建的對(duì)象。
          function myNew(context, ...argu{
            let obj = Object.create(null);
            obj.__proto = context.prototype;
            let res = context.apply(obj, argu);
            return typeof res === "object" ? res : obj;
          }

          大數(shù)相加

          let a = "123456789012345678";
          let b = "1";

          function add(a, b{
            //...
          }
          add(a, b); // '123456789012345679'

          思路:模擬加法運(yùn)算,但是需要用'0'補(bǔ)齊長(zhǎng)度,對(duì)于整數(shù),向前補(bǔ) 0。

          let a = "123456789012345678";
          let b = "1";
          // 1. 先找出最大的長(zhǎng)度的數(shù)
          // 2. 給較小的數(shù)填充向前填充0
          function add(a, b{
            let maxLength = Math.max(a.length, b.length);
            a = a.padStart(maxLength, "0");
            b = b.padStart(maxLength, "0");
            // 123456789012345678
            // 000000000000000001
            let res = ""// 返回的值
            let sum = 0// 同位相加的和
            let t = 0// 同位相加和的十位數(shù)
            let r = 0// 同位相加和的個(gè)位數(shù)
            for (let i = maxLength - 1; i >= 0; i--) {
              sum = parseInt(a[i]) + parseInt(b[i]) + t;
              t = Math.floor(sum / 10); // 拿到十位數(shù)的值
              r = sum % 10// 拿到個(gè)位數(shù)的值
              res = r + res;
            }
            return res;
          }

          console.log(add(a, b)); // 123456789012345679

          (滴滴)扁平化數(shù)組

          實(shí)現(xiàn)一個(gè) flat 函數(shù),接收 arr 和 depth 參數(shù)

          function flat(arr, depth = 1{
            return depth > 0
              ? arr.reduce((pre, cur) => {
                  return pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur);
                }, [])
              : arr.slice();
          }

          實(shí)現(xiàn)一個(gè) event 類(lèi)

          class EventEmitter {
            constructor() {}
            // 監(jiān)聽(tīng)事件
            on() {}
            // 觸發(fā)事件
            emit() {}
            // 只監(jiān)聽(tīng)一次,下次emit不會(huì)觸發(fā)
            once() {}
            // 移除事件
            off() {}
          }
          const events = new EventEmitter();
          events.on("hobby", (...argu) => {
            console.log("打游戲", ...argu);
          });
          let eat = () => {
            console.log("吃");
          };
          events.once("hobby", eat);
          events.on("hobby", (...argu) => {
            console.log("逛街", ...argu);
          });

          events.off("hobby", eat);
          events.emit("hobby"123);
          events.emit("hello""susan");
          //打游戲 1 2 3
          // 逛街 1 2 3

          答案:

          class EventEmitter {
            constructor() {
              this.events = {}; // 存放著所有的事件{eventName: [callback, ...]}
            }
            on(eventName, callback) {
              if (!this.events[eventName]) {
                this.events[eventName] = [callback];
              } else {
                this.events[eventName].push(callback);
              }
            }
            emit(eventName, ...argu) {
              if (this.events[eventName]) {
                this.events[eventName].forEach((fn) => fn(...argu));
              }
            }
            off(eventName, callback) {
              if (this.events[eventName]) {
                this.events[eventName] = this.events[eventName].filter(
                  (fn) => callback !== fn && fn.l !== callback
                );
              }
            }
            once(eventName, callback) {
              const _once = () => {
                callback();
                this.off(eventName, _once);
              };
              _once.l = callback;
              this.on(eventName, _once);
            }
          }

          實(shí)現(xiàn)千分位 format 函數(shù)

          // 接收一個(gè)number,返回一個(gè)string
          function format(number{}
          console.log(format(12345.789)); // 12,345.789,0
          console.log(format(0.12345678)); // 0.123,456,78
          console.log(format(123456)); // 123,456

          思路

          • 基于小數(shù)點(diǎn)切分,對(duì)于整數(shù)部分,從后往前遍歷,隔 3 加,
          • 小數(shù)點(diǎn)部分,從前往后便利,隔 3 加,
          function format(number{
            let str = number.toString();
            let [int, dec = ""] = str.split(".");
            let intStr = "";
            for (let i = int.length - 1; i >= 0; i--) {
              if ((int.length - i) % 3 === 0 && i !== 0) {
                intStr = "," + int[i] + intStr;
              } else {
                intStr = int[i] + intStr;
              }
            }
            let decStr = "";
            if (dec.length > 0) {
              for (let i = 0; i < dec.length; i++) {
                let sum = decStr + dec[i];
                if ((i + 1) % 3 === 0) {
                  decStr = sum + ",";
                } else {
                  decStr = sum;
                }
              }
            }
            return decStr.length > 0 ? `${intStr}.${decStr}` : `${intStr}`;
          }

          (字節(jié)、滴滴)根據(jù)傳入的姓名權(quán)重信息,返回隨機(jī)的姓名(隨機(jī)概率依據(jù)權(quán)重)

          第一次沒(méi)看懂題目,面試官解釋了一下。

          /**
           * @description: 根據(jù)傳入的姓名權(quán)重信息,返回隨機(jī)的姓名(隨機(jī)概率依據(jù)權(quán)重)
           * @param {Array} personValue
           * @returns {String} personName 姓名
           */

          var getPersonName = function (personValue{};
          const person = [
            {
              name"張三",
              weight1,
            },
            {
              name"李四",
              weight10,
            },
            {
              name"王五",
              weight100,
            },
          ];
          function getResult(count{
            const res = {};
            for (let i = 0; i < count; i++) {
              const name = getPersonName(person);
              res[name] = res[name] ? res[name] + 1 : 1;
            }

            console.log(res);
          }
          getResult(10000);

          答案:

          var getPersonName = function (personValue{
            // 標(biāo)記區(qū)間,并且獲得weight的總數(shù)
            let sum = personValue.reduce((pre, cur) => {
              cur.startW = pre;
              return (cur.endW = cur.weight + pre);
            }, 0);
            let s = Math.random() * sum; // 獲得一個(gè) 0 - 111 的隨機(jī)數(shù)
            // 判斷隨機(jī)數(shù)的所屬區(qū)間
            let person = personValue.find((item) => item.startW < s && s <= item.endW);
            return person.name;
          };

          (字節(jié))實(shí)現(xiàn)一個(gè) promise.all

          Promise.all = function (promises{
            let result = [];
            let count = 0;
            return new Promise((resolve, reject) => {
              promises.forEach((p, index) => {
                // 兼容不是promise的情況
                Promise.resolve(p)
                  .then((res) => {
                    result[index] = res;
                    count++;
                    if (count === promises.length) {
                      resolve(result);
                    }
                  })
                  .catch((err) => {
                    reject(err);
                  });
              });
            });
          };

          (滴滴)兩數(shù)之和

          力扣原題:兩數(shù)之和[31]
          剛開(kāi)始用雙重循環(huán)寫(xiě)了一個(gè),時(shí)間復(fù)雜度是 O(n^2),面試官問(wèn)你能優(yōu)化到 O(n)嗎?然后有了下面這個(gè)。

          var twoSum = function (nums, target{
            let len = nums.length;
            let map = new Map();
            for (let i = 0; i < len; i++) {
              if (map.has(target - nums[i])) {
                return [map.get(target - nums[i]), i];
              } else {
                map.set(nums[i], i);
              }
            }
          };

          console.log(twoSum([271115], 9));

          (字節(jié))無(wú)重復(fù)最長(zhǎng)子串

          力扣原題:無(wú)重復(fù)最長(zhǎng)子串[32]

          var lengthOfLongestSubstring = function (s{
            let arr = [];
            let max = 0;
            for (let i = 0; i < s.length; i++) {
              let index = arr.indexOf(s[i]);
              if (index !== -1) {
                arr.splice(0, index + 1);
              }
              arr.push(s.charAt(i));
              max = Math.max(arr.length, max);
            }
            return max;
          };

          實(shí)現(xiàn) new Queue 類(lèi)

          new Queue()
          .task(1000,()=>console.log(1))
          .task(2000,()=>console.log(2))
          .task(3000,()=>console.log(3)).start();
          實(shí)現(xiàn)一個(gè)Queue函數(shù),調(diào)用start之后,1s后打印1,接著2s后打印2,然后3s后打印3

          答案

          function sleep(delay, callback{
            return new Promise((resolve, reject) => {
              setTimeout(() => {
                callback();
                resolve();
              }, delay);
            });
          }
          class Queue {
            constructor() {
              this.listenser = [];
            }
            task(delay, callback) {
              // 收集函數(shù)
              this.listenser.push(() => sleep(delay, callback));
              return this;
            }
            async start() {
              // 遍歷函數(shù)
              for (let l of this.listenser) {
                await l();
              }
            }
          }

          new Queue()
            .task(1000, () => console.log(1))
            .task(2000, () => console.log(2))
            .task(3000, () => console.log(3))
            .start();

          總結(jié)

          還出了很多場(chǎng)景題,讓你給出解決方案,中間經(jīng)歷的太久了,面試記錄做的不夠好,很多問(wèn)題都忘了。一面基本上都是問(wèn)基礎(chǔ),后面幾輪面試會(huì)深入項(xiàng)目和要你給出解決方案。這次面試也是發(fā)現(xiàn)了自己的不足,平時(shí)寫(xiě)代碼的過(guò)程中,思考的不夠多,希望今后能多一些思考。

          參考資料

          [1]

          一份優(yōu)秀的前端開(kāi)發(fā)工程師簡(jiǎn)歷是怎么樣的: https://www.zhihu.com/question/23150301/answer/1229870117

          [2]

          HTTP 靈魂之問(wèn),鞏固你的 HTTP 知識(shí)體系: https://juejin.cn/post/6844904100035821575#heading-100

          [3]

          一張圖理解 http 緩存: https://segmentfault.com/a/1190000015816331

          [4]

          徹底搞懂 HTTPS 的加密原理: https://zhuanlan.zhihu.com/p/43789231

          [5]

          TCP 三次握手、四次揮手: https://juejin.cn/post/6844904194764177416

          [6]

          IEEE754 二進(jìn)制浮點(diǎn)運(yùn)算: https://www.h-schmidt.net/FloatConverter/IEEE754.html

          [7]

          JavaScript 的靜態(tài)作用域鏈與“動(dòng)態(tài)”閉包鏈: https://juejin.cn/post/6957913856488243237

          [8]

          面試官問(wèn):JS 的 this 指向: https://juejin.cn/post/6844903746984476686

          [9]

          ES6 入門(mén)教程: https://es6.ruanyifeng.com/

          [10]

          這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制: https://juejin.cn/post/6844903512845860872

          [11]

          你真的了解垃圾回收機(jī)制嗎: https://juejin.cn/post/6981588276356317214

          [12]

          前端需要掌握的設(shè)計(jì)模式: https://juejin.cn/post/6874906145463468046

          [13]

          瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘: https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

          [14]

          從 8 道面試題看瀏覽器渲染過(guò)程與性能優(yōu)化: https://juejin.cn/post/6844904040346681358

          [15]

          重排(reflow)和重繪(repaint): https://juejin.cn/post/6844904083212468238

          [16]

          combineReducers 源碼: https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts

          [17]

          一文吃透 react 事件系統(tǒng)原理: https://juejin.cn/post/6955636911214067720

          [18]

          React 性能優(yōu)化的 8 種方式了解一下: https://juejin.cn/post/6844903924302888973

          [19]

          React.memo: https://zh-hans.reactjs.org/docs/react-api.html#reactmemo

          [20]

          React.useMemo: https://zh-hans.reactjs.org/docs/hooks-reference.html#usememo

          [21]

          官網(wǎng)地址 useCallback: https://zh-hans.reactjs.org/docs/hooks-reference.html#usecallback

          [22]

          這可能是最通俗的 React Fiber(時(shí)間分片) 打開(kāi)方式: https://juejin.cn/post/6844903975112671239

          [23]

          簡(jiǎn)明 JavaScript 函數(shù)式編程——入門(mén)篇: https://juejin.cn/post/6844903936378273799

          [24]

          React.forwardRef: https://zh-hans.reactjs.org/docs/react-api.html#reactforwardref

          [25]

          帶你深度解鎖 Webpack 系列(優(yōu)化篇): https://juejin.cn/post/6844904093463347208

          [26]

          手把手教你寫(xiě)一個(gè) loader / plugin: https://juejin.cn/post/6976052326947618853

          [27]

          file-loader 源碼地址: https://github.com/webpack-contrib/file-loader/blob/master/src/index.js

          [28]

          飛書(shū)團(tuán)隊(duì) Webpack5 上手測(cè)評(píng): https://juejin.cn/post/6844904169405415432

          [29]

          輕松理解 webpack 熱更新原理: https://juejin.cn/post/6844904008432222215

          [30]

          運(yùn)算符優(yōu)先級(jí): https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FOperators%2FOperator_Precedence

          [31]

          兩數(shù)之和: https://leetcode-cn.com/problems/two-sum/

          [32]

          無(wú)重復(fù)最長(zhǎng)子串: https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

          瀏覽 46
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩免费电影三级片网站 | 日本无码色情三级播放免费看电影 | 黄 色 视 频高潮 | 久久免费短视频 | 天天日天天插天天干 |