2022 前端一场面试及答案整理
金三馬上就要開始了,俗話說得好,知己知彼百戰百勝,多準備總是沒有錯的。以面試的形式和大家一起學習、一起回顧我們的職場生涯。今天簡單總結一下我個人去面試,包括我在面試別人時的經驗。加油加加油!!!
目錄
開頭熱場問題
1. 說一下工作中解決過比較困難的問題 或 說一下自己項目中比較有亮點的地方
2. 你了解瀏覽器的事件循環嗎?
? ? 2.1 為什么 js 在瀏覽器中有事件循環的機制?
? ? 2.2 你了解事件循環當中的兩種任務嗎?
? ? 2.3 為什么要引入微任務的概念,只有宏任務可以嗎?
? ? 2.4 你了解 Node.js 的事件循環嗎?Node中的事件循環和瀏覽器的事件循環有什么區別?
? ? ? ? 2.5 這個時候理論問了這么多了,開始實踐1
? ? ? ? 2.6 實踐2
? ? ?2.7 實踐3?
3. 事件的捕獲和冒泡機制你了解多少?
? ? 3.1 基本概念
? ? 3.2? window.addEventListener 監聽的是什么階段的事件?? ? ? ? ?
? ? 3.3 平常有哪些場景用到了這些機制??
4.? 你工作中用過防抖和節流嗎?
? ? 4.1 基本概念
? ? 4.2 分別適合用在什么場景?
? ? 4.3? 手寫節流函數
5. 你了解 Promise 嗎?平時用的多嗎?
? ? 5.1 Promise.all( ) 你知道有什么特性嗎?
? ? 5.2?如果其中一個 Promise 報錯了怎么辦?
? ? 5.3?如果有一個 Promise 報錯了,那么其他的 Promise 還會執行嗎?
? ? 5.4 手寫一個?Promise.all(? )
? ? 5.5 Promise 在初始化的時候已經執行了,那么利用這個特性我們可以做點什么(擴展性問題)?
?6. 字節經典算法題---- 接雨水
開頭熱場問題
1. 說一下工作中解決過比較困難的問題 或 說一下自己項目中比較有亮點的地方
考察目的:面試官主要看一下我們解決問題的能力
解答:這個問題 主要是靠大家的工作積累,平時工作過程中可以養成一個良好的習慣,無論做什么需求的時候,都花點時間去記錄一下,這樣積累無論是一年,還是兩年,或者五年都會積累很多也無需求,然后自己梳理一下,在面試的時候反饋給面試官,讓面試官虎軀一震,offer馬上到手~~~
2. 你了解瀏覽器的事件循環嗎?
考察目的:以此問題作為一個突破口,深度挖掘你對整個概念的了解
2.1 為什么 js 在瀏覽器中有事件循環的機制?
解答:
① JS 是單線程的
② event loop
2.2 你了解事件循環當中的兩種任務嗎?
解答:
① 宏任務:整體代碼塊、setTimeOut、setInterval、I/O操作
② 微任務:new Promise().then()、mutationObserver(前端的回溯)
2.3 為什么要引入微任務的概念,只有宏任務可以嗎?
解答:
宏任務:先進先出的原則
在 JS 的執行過程中 或者說在頁面的渲染過程中,宏任務是按照先進先出的原則執行,我們并不能準確的控制 這些任務被推進任務隊列里,但是我們這個時候如果出來一個非常高優先級的任務,這個時候該怎么辦?如果我們只有宏任務,再往任務隊列里面推一個,秉承著先進先出的原則,那么它肯定是最后一個執行的,所以要引入微任務;
了解到宏任務與微任務過后,我們來學習宏任務與微任務的執行順序。
- 代碼開始執行,創建一個全局調用棧,script作為宏任務執行
- 執行過程過同步任務立即執行,異步任務根據異步任務類型分別注冊到微任務隊列和宏任務隊列
- 同步任務執行完畢,查看任務隊列
- 若存在微任務,將微任務隊列全部執行(包括執行微任務過程中產生的新微任務)
- 若無微任務,查看宏任務隊列,執行第一個宏任務,宏任務執行完畢,查看微任務隊列,重復上述操作,直至宏任務隊列為空
2.4 你了解 Node.js 的事件循環嗎?Node中的事件循環和瀏覽器的事件循環有什么區別?
解答:
Node宏任務的執行順序:
① timers定時器:執行已經安排過的,setTimeout 和 setInterval 的回調函數;
②pending callback 待定回調:執行延遲到下一個循環迭代的I/O回調;
③idle,prepare:僅系統內部使用;
④poll:檢索新的I/O事件,執行與I/O相關的回調
⑤check:執行setImmediate() 回調函數
⑥close callback:socket.on(‘close’, ( )=>{ })
微任務和宏任務在node的執行順序:
首先大家要明白,Node中微任務和宏任務的執行順序是和 node 的版本有關系的
Node V10 之前:
① 執行完上述一個階段中的所有任務
② 執行 nextTick 隊列里面的內容
③ 執行完微任務隊列的內容
Node V10 之后:
和瀏覽器的行為統一了
2.5 這個時候理論問了這么多了,開始實踐1
async function async1() {console.log("async1 start");await async2();console.log("async1 end"); } async function async2() {console.log("async2"); } console.log("script start"); setTimeout(() => {console.log("setTimeout"); }, 0); async1(); new Promise((resolve) => {console.log("promise1");resolve(); }).then(() => {console.log("promise2"); }); console.log("script end");// 1. script start // 2. async1 start // 3. async2 // 4. promise1 // 5. script end // 6. async1 end // 7. promise2 // 8. setTimeout2.6 實踐2
這個就比較有難度了,如果真的不會,千萬不要張嘴就來一句:“我不會”,這個時候我作為面試官的時候心里就在想:“我屮艸芔茻,這好尷尬”!娛樂一下開個玩笑,這個時候要嘗試著說出自己的思路,即使是錯的,你也要讓面試官看到你的 進取、鉆研精神!
console.log("start"); setTimeout(() => {console.log("children2");Promise.resolve().then(() => {console.log("children3");}); }, 0);new Promise((resolve, reject) => {console.log("children4");setTimeout(() => {console.log("children5");resolve("children6"); // 此處大坑}, 0); }).then((res) => {console.log("children7");setTimeout(() => {console.log(res);}, 0); });// 1. start // 2. children4 /** 第一輪宏任務執行結束,嘗試清空微任務隊列,發現沒有微任務,嘗試執行下一輪宏任務 */ // 3. children2 /** 第二輪宏任務執行結束,嘗試清空微任務隊列, */ // 4. children3 // 5. children5 /** 第三輪宏任務執行結束,嘗試清空微任務隊列, */ // 6. children7 // 7. children62.7 實踐3
到這一步,大家心里是不是在想:“面試官怎么抓著 事件循環不放了”,最后一道題
const p = () => {return new Promise((resolve, reject) => {const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve(1); // 這里 不會再輸出了,因為 resolve(2) 已經把結果輸出}, 0);resolve(2);});p1.then((res) => {console.log(res);});console.log(3);resolve(4);}); }; p().then((res) => {console.log(res); }); console.log("end");// 1. 3 // 2. end // 3. 2 // 4. 4到此為止,事件循環結束!
3. 事件的捕獲和冒泡機制你了解多少?
3.1 基本概念
以HTML為例:↓
捕獲:從window → parent → child → son 到目標元素以后,轉為冒泡
冒泡:目標元素 son→ child→ parent→window
3.2 window.addEventListener 監聽的是什么階段的事件?
// 冒泡階段 window.addEventListener("click", () => {}); // 第三個參數默認為 false,為false 時,監聽的為冒泡階段// 捕獲階段 window.addEventListener("click", () => {}, true);3.3 平常有哪些場景用到了這些機制?
3.3.1. 事件委托
<ul id="ul"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>const ul = document.querySelector("ul");ul.addEventListener("click", (e) => {const target = e.target;if (target.tagName.toLowerCase() === "li") {const liList = document.querySelectorAll("li");// 這里之所以會這么寫,是因為 liList 并非是一個真正的 Array// 此時返回的是一個 nodeList,如果想使用 數組的 方法,需要改變thisconst index = Array.prototype.indexOf.call(liList, target);console.log(`內容:${target.innerHTML},索引:${index}`);}});3.3.2 場景設計題
一個歷史頁面,上面有若干按鈕的點擊邏輯,每個按鈕都有自己的 click 事件
新需求來了:給每一個訪問的用戶添加了一個屬性,如果 banned = true,此用戶點擊頁面上的任何按鈕或元素,都不可響應原來的函數。而是直接 alert 提示,你被封禁了。
實現:采用 事件捕獲 機制完成(當然實現的思路有三種,甚至更多,這里只說事件捕獲)
/*** 場景設計題 ????一個歷史頁面,上面有若干按鈕的點擊邏輯,每個按鈕都有自己的 click 事件 ? ? ?新需求來了:給每一個訪問的用戶添加了一個屬性,如果 banned = true,此用戶點擊頁面上的任何按鈕或元素,都不可響應原來的函數。而是直接 alert 提示,你被封禁了。 */window.addEventListener("click",(e) => {if (banned) {e.stopPropagation();}},true );4. 你工作中用過防抖和節流嗎?
4.1 基本概念
防抖:持續觸發事件的時候,一定時間段內,沒有再觸發事件,時間處理函數才會執行一次
節流:持續觸發事件的時候,保證一段時間內只調用一次事件處理函數(固定時間)
4.2 分別適合用在什么場景?
防抖:input輸入(巨量引擎)
節流:resize(屏幕大小改變)、scroll(滾動時) —> 一定要執行的,給一個固定間隔
4.3 手寫節流函數
時間戳寫法,第一次立即執行
// 時間戳寫法,第一次立即執行 const throttle = (fn, interval) => {let last = 0;return () => {let now = Date.now();if (now - last >= interval) {fn.apply(this, arguments);}}; }; const handle = () => {console.log(Math.random()); }; const throttleHandle = throttle(handle, 3000); throttleHandle(); throttleHandle();定時器寫法,第一次也需要延時 具體 時間以后再執行
// 定時器寫法,第一次也會延時 具體的時間執行 const throttle = (fn, interval) => {let timer = null;return function () {let context = this;let args = arguments;if (!timer) {timer = setTimeout(() => {fn.apply(context, args);timer = null;}, interval);}}; }; const handle = () => {console.log(Math.random()); }; const throttleHandle = throttle(handle, 1000); throttleHandle(); throttleHandle();精確的實現一個節流函數,無論是第一次之后還是最后一次(避免最后一次執行還會再等具體時間之后再執行)
// 精確的實現一個節流函數,無論是第一次之后還是最后一次(避免最后一次執行還會再等具體時間之后再執行) const throttle = (fn, delay) => {let timer = null;let startTime = Date.now();return function () {let curTime = null;let remainning = delay - (curTime - startTime);let context = this;let args = arguments;clearTimeout(timer);if (remainning <= 0) {fn.apply(context, args);startTime = Date.now();} else {timer = setTimeout(fn, remainning);}}; };5. 你了解 Promise 嗎?平時用的多嗎?
5.1 Promise.all( ) 你知道有什么特性嗎?
解答:Promise.all( ) 會接受一個 Promise 數組,數組里面可以是 Promise 也可以是一個常量或者其他;執行情況為:Promise 里面的所有 Promise 執行完成以后才會返回結果;
5.2如果其中一個 Promise 報錯了怎么辦?
解答:如果有一個報錯了,那么整個 Promise.all( ),就會返回一個 catch
5.3如果有一個 Promise 報錯了,那么其他的 Promise 還會執行嗎?
解答:會的,因為 Promise 是在創建之初(實例化) 的時候已經執行了
5.4 手寫一個Promise.all( )
面試官:“給你 三個 如下的 Promise ,調用你的Promise.all( )以后,看是否會在三秒以內返回對應的結果”
// 測試 const pro1 = new Promise((resolve, reject) => {setTimeout(() => {resolve("1");}, 1000); }); const pro2 = new Promise((resolve, reject) => {setTimeout(() => {resolve("3");}, 2000); }); const pro3 = new Promise((resolve, reject) => {setTimeout(() => {resolve("3");}, 3000); });// 測試題 const PromiseAll = (promiseArray) => {};如何實現?
考點1:Promise.all( ) 里面的參數有可能不是一個 Promise,如何處理?
考點2:Promise.all( ) 返回值的順序,是你傳入的 Promise 順序,如何處理?
// 測試題 const PromiseAll = (promiseArray) => {return new Promise((resolve, reject) => {if (!Array.isArray(promiseArray)) {return reject(new Error("Type can only be array"));}const result = []; // promise 執行的結果集const promiseNums = promiseArray.length; // 當前循環次數let counter = 0; // 記錄當前 promise 執行順序,需要按照 傳入的 promise 順序返回for (let i = 0; i < promiseNums; i++) {Promise.resolve(promiseArray[i]).then((value) => {counter++;result.push(value);if (counter === promiseNums) {resolve(result);}}).catch((e) => reject(e));}}); };console.log(PromiseAll([pro1, pro2, pro3]).then((res) => {console.log(res);}).catch((e) => {console.log(e);}) );5.5 Promise 在初始化的時候已經執行了,那么利用這個特性我們可以做點什么(擴展性問題)?
解答:可以利用 promise 的這個特性做緩存;
利用 裝飾器 + Map結構,實現一個 Promise 的緩存;
const cacheMap = new Map(); const enableCache = (target, name, descriptor) => {const val = descriptor.value;descriptor.value = async (...args) => {const cacheKey = name + JSON.stringify(args);if (!cacheMap.get(cacheKey)) {const cacheValue = Promise.resolve(val.apply(this, args)).catch((_) => {cacheMap.set(cacheKey, null);});cacheMap.set(cacheKey, cacheValue);}return cacheMap.get(cacheKey);};return descriptor; }; class PromiseClass {@enableCachestatic async getInfo() {} }PromiseClass.getInfo(); // 第一次發送請求 PromiseClass.getInfo(); // 第二次以后就是緩存 PromiseClass.getInfo(); PromiseClass.getInfo();6. 字節經典算法題---- 接雨水
題干:
給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之后能接多少雨水。
示例1:
輸入 height = [0,1,0,2,1,0,1,3,2,1,2,1]
輸出:6
解釋:上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 單位的雨水(如上圖,藍色部分表示雨水)
示例2:
輸入 height = [4,2,0,3,2,5]
輸出:9
總結
以上是生活随笔為你收集整理的2022 前端一场面试及答案整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 显卡优化软件测试面试,世界顶级优化软件测
- 下一篇: 『转』VC 开机自动启动程序代码