前端十幾道含答案的大廠面試題總結(jié)
年底了,又到了跳槽季啦,該刷題走起了。這里總結(jié)了一些被問到可能會懵逼的面試真題,有需要的可以看下~
1. 說說JavaScript中有哪些異步編程方式?
1. 回調(diào)函數(shù)
f1(f2);
回調(diào)函數(shù)是異步編程的基本方法。其優(yōu)點是易編寫、易理解和易部署;缺點是不利于代碼的閱讀和維護,各個部分之間高度耦合 (Coupling),流程比較混亂,而且每個任務(wù)只能指定一個回調(diào)函數(shù)。
2. 事件監(jiān)聽
f1.on('done',f2);
事件監(jiān)聽即采用事件驅(qū)動模式,任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個事件是否發(fā)生。其優(yōu)點是易理解,可以綁定多個事件,每個事件可以指定多個回調(diào)函數(shù),可以去耦合, 有利于實現(xiàn)模塊化;缺點是整個程序都要變成事件驅(qū)動型,運行流程會變得不清晰。
3. 發(fā)布/訂閱
f1: jQuery.publish("done");
f2: jQuery.subscribe("done", f2);
假定存在一個"信號中心",某個任務(wù)執(zhí)行完成,就向信號中心"發(fā)布"(publish)一個信號,其他任務(wù)可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行,這就叫做 "發(fā)布/訂閱模式" (publish-subscribe pattern),又稱 "觀察者模式" (observer pattern)。該 方法的性質(zhì)與"事件監(jiān)聽"類似,但其優(yōu)勢在于可以 通過查看"消息中心",了解存在多少信號、每個信號有多少訂閱者,從而監(jiān)控程序的運行。
4. promise對象
f1().then(f2);
Promises對象是CommonJS工作組提出的一種規(guī)范,目的是為異步編程提供 統(tǒng)一接口 ;思想是, 每一個異步任務(wù)返回一個Promise對象,該對象有一個then方法,允許指定回調(diào)函數(shù)。其優(yōu)點是回調(diào)函數(shù)是鏈?zhǔn)綄懛?,程序的流程非常清晰,而且有一整套的配套方法?可以實現(xiàn)許多強大的功能,如指定多個回調(diào)函數(shù)、指定發(fā)生錯誤時的回調(diào)函數(shù), 如果一個任務(wù)已經(jīng)完成,再添加回調(diào)函數(shù),該回調(diào)函數(shù)會立即執(zhí)行,所以不用擔(dān)心是否錯過了某個事件或信號;缺點就是編寫和理解相對比較難。
2. 有哪些監(jiān)控網(wǎng)頁卡頓的方法?
卡頓
網(wǎng)頁的 FPS
網(wǎng)頁內(nèi)容在不斷變化之中,網(wǎng)頁的 FPS 是只瀏覽器在渲染這些變化時的幀率。幀率越高,用戶感覺網(wǎng)頁越流暢,反之則會感覺卡頓。
監(jiān)控卡頓方法
每秒中計算一次網(wǎng)頁的 FPS 值,獲得一列數(shù)據(jù),然后分析。通俗地解釋就是,通過 requestAnimationFrame API 來定時執(zhí)行一些 JS 代碼,如果瀏覽器卡頓,無法很好地保證渲染的頻率,1s 中 frame 無法達到 60 幀,即可間接地反映瀏覽器的渲染幀率。
var lastTime = performance.now();
var frame = 0;
var lastFameTime = performance.now();
var loop = function(time) {
var now = performance.now();
var fs = (now - lastFameTime);
lastFameTime = now;
var fps = Math.round(1000/fs);
frame++;
if (now > 1000 + lastTime) {
var fps = Math.round( ( frame * 1000 ) / ( now - lastTime ) );
frame = 0;
lastTime = now;
};
window.requestAnimationFrame(loop);
}
我們可以定義一些邊界值,比如連續(xù)出現(xiàn)3個低于20的 FPS 即可認為網(wǎng)頁存在卡頓。
3. 說說script 標(biāo)簽中的defer 和 async 異同點?
defer
這個屬性的用途是表明腳本在執(zhí)行時不會影響頁面的構(gòu)造。也就是說,腳本會被延遲到整個頁面都解析完畢后再運行。因此,在script元素中設(shè)置defer屬性,相當(dāng)于告訴瀏覽器立即下載,但延遲執(zhí)行。
HTML5規(guī)范要求腳本按照它們出現(xiàn)的先后順序執(zhí)行,因此第一個延遲腳本會先于第二個延遲腳本執(zhí)行,而這兩個腳本會先于DOMContentLoaded事件執(zhí)行。在現(xiàn)實當(dāng)中,延遲腳本并不一定會按照順序執(zhí)行,也不一定會在DOMContentLoad時間觸發(fā)前執(zhí)行,因此最好只包含一個延遲腳本。
對于不支持的瀏覽器,如safari,并不會延遲執(zhí)行,還是會阻塞瀏覽器渲染。
async
這個屬性與defer類似,都用于改變處理腳本的行為。同樣與defer類似,async只適用于外部腳本文件,并告訴瀏覽器立即下載文件。但與defer不同的是,標(biāo)記為async的腳本并不保證按照它們的先后順序執(zhí)行。
第二個腳本文件可能會在第一個腳本文件之前執(zhí)行。因此確保兩者之間互不依賴非常重要。指定async屬性的目的是不讓頁面等待兩個腳本下載和執(zhí)行,從而異步加載頁面其他內(nèi)容。
總結(jié)
async 和 defer 雖然都是異步的,不過還有一些差異,使用 async 標(biāo)志的腳本文件一旦加載完成,會立即執(zhí)行;而使用了 defer 標(biāo)記的腳本文件,需要在 DOMContentLoaded 事件之前執(zhí)行。
可以再擴展一下:link preload也可以用于js提前加載,和上述兩者的區(qū)別? 還有應(yīng)用場景上的建議
4. margin:auto 為什么可以實現(xiàn)垂直居中?
margin概念:
margin屬性為給定元素設(shè)置所有四個(上下左右)方向的外邊距屬性。這是四個外邊距屬性設(shè)置的簡寫。四個外邊距屬性設(shè)置分別是: margin-top,margin-right,margin-bottom和margin-left。指定的外邊距允許為負數(shù)。
margin的top和bottom屬性對非替換內(nèi)聯(lián)元素?zé)o效,例如span和 code。
實現(xiàn)垂直居中
想要實現(xiàn)垂直方向的居中可以用絕對定位:
div {
width: 20px;
height: 20px;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
為什么能實現(xiàn)垂直居中
塊狀水平元素,如div元素(下同),在默認情況下(非浮動、絕對定位等),水平方向會自動填滿外部的容器;如果有margin-left/margin-right,padding-left/padding-right,border-left-width/border-right-width等,實際內(nèi)容區(qū)域會響應(yīng)變窄。
但是,當(dāng)一個絕對定位元素,其對立定位方向?qū)傩酝瑫r有具體定位數(shù)值的時候,流體特性就發(fā)生了。具有流體特性絕對定位元素的margin:auto的填充規(guī)則和普通流體元素一模一樣,含有以下特性:
- 如果一側(cè)定值,一側(cè)auto,auto為剩余空間大??;
- 如果兩側(cè)均是auto, 則平分剩余空間
5. Cdn有哪些優(yōu)化靜態(tài)資源加載速度的方法?
可以參考阿里云團隊的《CDN之我見》??偨Y(jié)如下:
資源調(diào)度:CDN會根據(jù)用戶接入網(wǎng)絡(luò)的ip尋找距離用戶最優(yōu)路徑的服務(wù)器。調(diào)度的方式主要有DNS調(diào)度、http 302調(diào)度、使用 HTTP 進行的 DNS 調(diào)度(多用于移動端); 緩存策略和數(shù)據(jù)檢索:CDN服務(wù)器使用高效的算法和數(shù)據(jù)結(jié)構(gòu),快速的檢索資源和更新讀取緩存; 網(wǎng)絡(luò)優(yōu)化:從OSI七層模型進行優(yōu)化,達到網(wǎng)絡(luò)優(yōu)化的目的。 L1物理層:硬件設(shè)備升級提高速度 L2數(shù)據(jù)鏈路層:尋找更快的網(wǎng)絡(luò)節(jié)點、確保 Lastmile 盡量短 L3路由層:路徑優(yōu)化,尋找兩點間最優(yōu)路徑 L4傳輸層:協(xié)議TCP優(yōu)化,保持長連接、TCP快速打開 L7應(yīng)用層:靜態(tài)資源壓縮、請求合并
6. fetch 和 ajax 的區(qū)別?
Ajax 技術(shù)的核心是XMLHttpRequest 對象(簡稱XHR)。 XHR 為向服務(wù)器發(fā)送請求和解析服務(wù)器響應(yīng)提供了流暢的接口。能夠以異步方式從服務(wù)器取得更多信息,意味著用戶單擊后,可以不必刷新頁面也能取得新數(shù)據(jù)。 看一個調(diào)用例子:
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "example.txt", true);
xhr.send(null);
fetch號稱是ajax的替代品,它的API是基于Promise設(shè)計的,舊版本的瀏覽器不支持Promise,需要使用polyfill es6-promise,舉個例子:
// 原生XHR
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText) // 從服務(wù)器獲取數(shù)據(jù)
}
}
xhr.send()
// fetch
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => console.log(data))
.catch(err => console.log(err))
看起來好像是方便點,then鏈就像之前熟悉的callback。 在MDN上,講到它跟jquery ajax的區(qū)別,這也是fetch很奇怪的地方:
當(dāng)接收到一個代表錯誤的 HTTP 狀態(tài)碼時,從 fetch()返回的 Promise 不會被標(biāo)記為 reject, 即使該 HTTP 響應(yīng)的狀態(tài)碼是 404 或 500。相反,它會將 Promise 狀態(tài)標(biāo)記為 resolve (但是會將 resolve 的返回值的 ok 屬性設(shè)置為 false ), 僅當(dāng)網(wǎng)絡(luò)故障時或請求被阻止時,才會標(biāo)記為 reject。 默認情況下, fetch 不會從服務(wù)端發(fā)送或接收任何 cookies, 如果站點依賴于用戶 session,則會導(dǎo)致未經(jīng)認證的請求(要發(fā)送 cookies,必須設(shè)置 credentials 選項).
7. 求代碼輸出值(函數(shù)/原型 指向問題)?
2
函數(shù)也是一個 object, 可以擁有屬性和方法, Foo.getName 被賦值為一個輸出 2 的函數(shù), 所以輸出 2
Foo.getName = function () { console.log(2) }
Foo.getName() // 輸出 2
4
getName() 父級作用域為 window, 相當(dāng)于調(diào)用 window.getName(), Foo() 還未被執(zhí)行, 不需要考慮 Foo 函數(shù)體內(nèi)對 getName 的影響, 剩下最后兩個
var getName = function () { console.log(4) } // 函數(shù)表達式
function getName() { // 函數(shù)聲明
console.log(5)
}
函數(shù)聲明會提升, 也就是即使在后邊聲明的函數(shù), 在前別也能調(diào)用, 如:
getName() // 輸出 5
function getName() { // 函數(shù)聲明
console.log(5)
}
但題中函數(shù)表達式會覆蓋函數(shù)聲明, 來看下邊這段:
getName() // 輸出 5, 函數(shù)聲明提升的結(jié)果
var getName = function () { console.log(4) } // 函數(shù)表達式
getName() // 輸出 4, 函數(shù)聲明提升后, 又被函數(shù)表達式覆蓋了
function getName() { // 函數(shù)聲明
console.log(5)
}
getName() // 還是輸出 4, 覆蓋的結(jié)果, 函數(shù)聲明提升了, 不按照文本的順序重新聲明的, 你可以想象它被移到了最前邊
1
Foo().getName()
我們一步步拆解, 上邊的語句相當(dāng)于
var context = Foo(); context.getName();
來看 Foo 的聲明:
function Foo() {
// 下邊這句沒有 var, let 或 const, 相當(dāng)于 window.getName = xxx
getName = function () { console.log(1) }
return this // 這里的 this 要看調(diào)用方式, 直接調(diào)用 Foo() 則 this 指向 window, new 調(diào)用, this 指向 new 出來的實例
}
仔細看上邊的注釋, Foo 函數(shù)體內(nèi)對 window.getName 進行了改寫, 這是下一個輸出的關(guān)鍵
1
如上邊分析的, Foo() 函數(shù)的執(zhí)行, 對 window.getName 進行了改寫, window.getName 此時已經(jīng)變?yōu)?function () { console.log(1) }
2
new Foo.getName()
該語句先執(zhí)行 Foo.getName, 與第一個結(jié)論一致, 輸出 2, 只是 new 會返回一個 object, 這個 object 指向 new 出來的實例, 但這里這個實例沒被使用, 就不進一步分析了
3
new Foo().getName()
拆解如下
var foo = new Foo() foo.getName()
如果你是一路看分析下來的, 就會明白 foo 這個實例, 就是 Foo 函數(shù)體里的 this. 從原型的知識中, 我們可以知道, 如果調(diào)用一個實例的方法, 在實例方法中找不到, 就會從實例原型中找.
也就是會找到下邊這個方法, 并執(zhí)行:
Foo.prototype.getName = function() { console.log(3) }
3
new new Foo().getName()
拆解如下
var foo = new Foo(); var bar = new foo.getName();
從上邊 new Foo().getName() 的分析, 可以知道 foo.getName() 是在 foo 的原型里邊的, 這里 new 了一下原型里邊的函數(shù), 相當(dāng)于先執(zhí)行了, 再返回了一個新的實例. 這里的執(zhí)行, 也就是執(zhí)行了下邊這個方法:
Foo.prototype.getName = function() { console.log(3) }
只是 new 額外返回一個實例, 實例沒被使用, 沒什么特殊的.
如果還有不明白的地方, 建議閱讀 《JavaScript 語言精髓》第四章 (本人看的是修訂版) 關(guān)于函數(shù)調(diào)用相關(guān)的內(nèi)容. 相關(guān)電子書可以微信關(guān)注公眾號 前端Q, 回復(fù) ebook 閱讀. 此外, 建議購買正版.
8. 如何判斷左右小括號是否全部匹配。如 ( ( ))()((((()))))?
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
var stack = []
var map = {
'(' : ')',
'[': ']',
'{': '}'
}
for (var char of s) {
if(char in map) {
stack.push(char)
} else {
if( !stack.length || char != map[stack.pop()]) {
return false
}
}
}
// 如果最后stack 里沒有元素了, 就一定是匹配的
return !stack.length
};
9. 用css畫一個扇形?
width: 0; height: 0; border: solid 100px red; border-color: red transparent transparent transparent; border-radius: 100px;
10. 用css實現(xiàn)已知或者未知寬度的垂直水平居中?
/
/ 1
.wraper {
position: relative;
.box {
position: absolute;
top: 50%;
left: 50%;
width: 100px;
height: 100px;
margin: -50px 0 0 -50px;
}
}
// 2
.wraper {
position: relative;
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
// 3
.wraper {
.box {
display: flex;
justify-content:center;
align-items: center;
height: 100px;
}
}
// 4
.wraper {
display: table;
.box {
display: table-cell;
vertical-align: middle;
}
}
//5
.container {
display: grid;
grid-auto-columns: 1fr;
grid-auto-rows: 200px;
background: #eee;
}
.parent {
background: grey;
justify-self: center;
align-self: center;
}
.child {
font-size: 30px;
}
//6、塊級元素:calc()
.parent {
width: 300px;
height: 300px;
border: 1px solid red;
position: relative;
}
.child {
width: 100px;
height: 100px;
background: blue;
padding: -webkit-calc((100% - 100px) / 2);
padding: -moz-calc((100% - 100px) / 2);
padding: -ms-calc((100% - 100px) / 2);
padding: calc((100% - 100px) / 2);
background-clip: content-box;
}
//7、margin:auto實現(xiàn)絕對定位元素的居中
.element {
width: 600px; height: 400px;
position: absolute; left: 0; top: 0; right: 0; bottom: 0;
margin: auto; /* 有了這個就自動居中了 */
}
//8、
.parent{
display: flex;
}
.parent{
display: flex;
width: 500px;
height: 500px;
background-color: pink;
}
.child{
flex: 0 0 auto;
margin: auto;
width: 100px;
height: 100px;
background-color: red;
11. 如何理解回流和重繪? ?
回流:當(dāng)我們對 DOM 的修改引發(fā)了 DOM 幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時,瀏覽器需要重新計算元素的幾何屬性(其他元素的幾何屬性和位置也會因此受到影響),然后再將計算的結(jié)果繪制出來。這個過程就是回流(也叫重排)。
重繪:當(dāng)我們對 DOM 的修改導(dǎo)致了樣式的變化、卻并未影響其幾何屬性(比如修改了顏色或背景色)時,瀏覽器不需重新計算元素的幾何屬性、直接為該元素繪制新的樣式(跳過了上圖所示的回流環(huán)節(jié))。這個過程叫做重繪。 由此我們可以看出,重繪不一定導(dǎo)致回流,回流一定會導(dǎo)致重繪。
常見的會導(dǎo)致回流的元素:
- 常見的幾何屬性有 width、height、padding、margin、left、top、border 等等。
- 最容易被忽略的操作:獲取一些需要通過即時計算得到的屬性,當(dāng)你要用到像這樣的屬性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 時,瀏覽器為了獲取這些值,也會進行回流。
- 當(dāng)我們調(diào)用了 getComputedStyle 方法,或者 IE 里的 currentStyle 時,也會觸發(fā)回流。原理是一樣的,都為求一個“即時性”和“準(zhǔn)確性”。
避免方式:
- 避免逐條改變樣式,使用類名去合并樣式
- 將 DOM “離線”,使用DocumentFragment
- 提升為合成層,如使用will-change
#divId {
will-change: transform;
}
優(yōu)點
- 合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
- 當(dāng)需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
- 對于 transform 和 opacity 效果,不會觸發(fā) layout 和 paint
注意:
部分瀏覽器緩存了一個 flush 隊列,把我們觸發(fā)的回流與重繪任務(wù)都塞進去,待到隊列里的任務(wù)多起來、或者達到了一定的時間間隔,或者“不得已”的時候,再將這些任務(wù)一口氣出隊。但是當(dāng)我們訪問一些即使屬性時,瀏覽器會為了獲得此時此刻的、最準(zhǔn)確的屬性值,而提前將 flush 隊列的任務(wù)出隊。
12. 手寫一個Promise?
答案太長,你可以參考這個issues:https://github.com/LuckyWinty/fe-weekly-questions/issues/20
13. 談?wù)剋eb安全問題及解決方案
答案太長,你可以參考這個issues:https://github.com/LuckyWinty/fe-weekly-questions/issues/1
14. HTTPS和HTTP有什么區(qū)別?
答案太長,你可以參考這個issues:https://github.com/LuckyWinty/fe-weekly-questions/issues/2
15. Webpack性能優(yōu)化你知道哪些?
答案太長,你可以參考這個issues:https://github.com/LuckyWinty/fe-weekly-questions/issues/4
更多
本期匯總暫時到這里,更多題目,可以關(guān)注: https://github.com/LuckyWinty/fe-weekly-questions
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
- 一場疫情過后,又要經(jīng)歷一次次面試,今天小編給大家分享2020前端面試題之HTML篇,非常不錯,對大家有所幫助,需要的朋友參考下吧2020-03-25
- 這篇文章主要介紹了2019大廠前端面試題小結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2020-03-05
- 隨著疫情的不斷好轉(zhuǎn),各地都開始逐步的復(fù)工,當(dāng)然對我們來說,也馬上迎來所謂的金三銀四跳槽季。今天小編給大家分享前端常見面試題,需要的朋友跟隨小編一起看看吧2020-02-27
- 這篇文章主要介紹了Web前端面試筆試題總結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2020-02-18
- 這篇文章主要介紹了80道前端面試經(jīng)典選擇題匯總,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)2020-01-08
- 這篇文章主要介紹了面試官常問的web前端問題大全,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-03
- 這篇文章主要介紹了超實用前端面試題整理(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-12-19

每個前端工程師都應(yīng)該去了解的前端面試題小結(jié)(推薦)
面試對于我們每個程序員來說都是非常重要的環(huán)節(jié),掌握一些面試題技巧是非常有必要的,今天小編給大家分享幾個js有關(guān)的面試題,需要的朋友參考下吧2020-04-15


