米利唐_后腰_乌克兰足球超级联赛_中国竞彩欧赔 - 足球竞彩分析

集團官網(wǎng)
  • 國家級全民數(shù)字素養(yǎng)與技能培訓(xùn)基地
  • 河南省第一批產(chǎn)教融合型企業(yè)建設(shè)培育單位
  • 鄭州市數(shù)字技能人才(碼農(nóng))培養(yǎng)評價聯(lián)盟

前端必備 | 10道瀏覽器面試題解析,你會做嗎?

編輯:云和數(shù)據(jù) 日期:2020-10-17 09:43

前言 Preface

想要成為一名合格的前端工程師,掌握相關(guān)瀏覽器的工作原理是必備的,這樣子才會有一個完整知識體系,要是「能參透瀏覽器的工作原理,你就能解決80%的前端難題」。

今天總結(jié)了10道瀏覽器面試題及解析,作為前端開發(fā)工程師的你趕緊來看看吧!

1. 常見的瀏覽器內(nèi)核有哪些?

2. 瀏覽器的主要組成部分是什么?

「用戶界面」 – 包括地址欄、前進/后退按鈕、書簽菜單等。

「瀏覽器引擎」 – 在用戶界面和呈現(xiàn)引擎之間傳送指令。

「呈現(xiàn)引擎」 – 負責(zé)顯示請求的內(nèi)容。如果請求的內(nèi)容是 HTML,它就負責(zé)解析 HTML 和 CSS 內(nèi)容,并將解析后的內(nèi)容顯示在屏幕上。

「網(wǎng)絡(luò)」 – 用于網(wǎng)絡(luò)調(diào)用,比如 HTTP 請求。

「用戶界面后端」 -用于繪制基本的窗口小部件,比如組合框和窗口。

「JavaScript 解釋器」– 用于解析和執(zhí)行 JavaScript 代碼。

「數(shù)據(jù)存儲」 – 這是持久層。瀏覽器需要在硬盤上保存各種數(shù)據(jù),例如 Cookie。新的 HTML 規(guī)范 (HTML5) 定義了“網(wǎng)絡(luò)數(shù)據(jù)庫”,這是一個完整(但是輕便)的瀏覽器內(nèi)數(shù)據(jù)庫。

值得注意的是,和大多數(shù)瀏覽器不同,Chrome 瀏覽器的每個標簽頁都分別對應(yīng)一個呈現(xiàn)引擎實例。每個標簽頁都是一個獨立的進程。

3. 為什么JavaScript是單線程的,與異步?jīng)_突嗎

補充:JS中其實是沒有線程概念的,所謂的單線程也只是相對于多線程而言。JS的設(shè)計初衷就沒有考慮這些,針對JS這種不具備并行任務(wù)處理的特性,我們稱之為“單線程”。

JS單線程是指一個瀏覽器進程中只有一個JS的執(zhí)行線程,同一時刻內(nèi)只會有一段代碼在執(zhí)行。

舉個通俗例子,假設(shè)JS支持多線程操作的話,JS可以操作DOM,那么一個線程在刪除DOM,另外一個線程就在獲取DOM數(shù)據(jù),這樣子明顯不合理,這算是證明之一。

來看段代碼??

function foo() {? ? console.log("first");

? ? setTimeout(( function(){? ? ? ? console.log( 'second' );

? ? }),5);

}?

for (var i = 0; i < 1000000; i++) {

? ? foo();

}

打印結(jié)果就是首先是很多個first,然后再是second。

異步機制是瀏覽器的兩個或以上常駐線程共同完成的,舉個例子,比如異步請求由兩個常駐線程,JS執(zhí)行線程和事件觸發(fā)線程共同完成的。

JS執(zhí)行線程發(fā)起異步請求(瀏覽器會開啟一個HTTP請求線程來執(zhí)行請求,這時JS的任務(wù)完成,繼續(xù)執(zhí)行線程隊列中剩下任務(wù))

然后在未來的某一時刻事件觸發(fā)線程監(jiān)視到之前的發(fā)起的HTTP請求已完成,它就會把完成事件插入到JS執(zhí)行隊列的尾部等待JS處理

再比如定時器觸發(fā)(settimeout和setinterval) 是由「瀏覽器的定時器線程」執(zhí)行的定時計數(shù),然后在定時時間把定時處理函數(shù)的執(zhí)行請求插入到JS執(zhí)行隊列的尾端(所以用這兩個函數(shù)的時候,實際的執(zhí)行時間是大于或等于指定時間的,不保證能準確定時的)。

所以這么說,JS單線程與異步更多是瀏覽器行為,之間不沖突。

4. CSS加載會造成阻塞嗎

先給出結(jié)論

CSS不會阻塞DOM解析,但會阻塞DOM渲染。

CSS會阻塞JS執(zhí)行,并不會阻塞JS文件下載

先講一講CSSOM作用

第一個是提供給 JavaScript 操作樣式表的能力

第二個是為布局樹的合成提供基礎(chǔ)的樣式信息

這個 CSSOM 體現(xiàn)在 DOM 中就是document.styleSheets。

由之前講過的瀏覽器渲染流程我們可以看出:

DOM 和 CSSOM通常是并行構(gòu)建的,所以「CSS 加載不會阻塞 DOM 的解析」。

然而由于Render Tree 是依賴DOM Tree和 CSSOM Tree的,所以它必須等到兩者都加載完畢后,完成相應(yīng)的構(gòu)建,才開始渲染,因此,「CSS加載會阻塞DOM渲染」。

由于 JavaScript 是可操縱 DOM 和 css 樣式 的,如果在修改這些元素屬性同時渲染界面(即 JavaScript 線程和 UI 線程同時運行),那么渲染線程前后獲得的元素數(shù)據(jù)就可能不一致了。

因此為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置 「GUI 渲染線程與 JavaScript 引擎為互斥」的關(guān)系。

有個需要注意的點就是:

「有時候JS需要等到CSS的下載,這是為什么呢?」

仔細思考一下,其實這樣做是有道理的,如果腳本的內(nèi)容是獲取元素的樣式,寬高等CSS控制的屬性,瀏覽器是需要計算的,也就是依賴于CSS。瀏覽器也無法感知腳本內(nèi)容到底是什么,為避免樣式獲取,因而只好等前面所有的樣式下載完后,再執(zhí)行JS。

JS文件下載和CSS文件下載是并行的,有時候CSS文件很大,所以JS需要等待。

因此,樣式表會在后面的 js 執(zhí)行前先加載執(zhí)行完畢,所以「css 會阻塞后面 js 的執(zhí)行」。

5. 為什么JS會阻塞頁面加載

先給出結(jié)論??

「JS阻塞DOM解析」,也就會阻塞頁面

這也是為什么說JS文件放在最下面的原因,那為什么會阻塞DOM解析呢

你可以這樣子理解:

由于 JavaScript 是可操縱 DOM 的,如果在修改這些元素屬性同時渲染界面(即 JavaScript 線程和 UI 線程同時運行),那么渲染線程前后獲得的元素數(shù)據(jù)就可能不一致了。

因此為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置 「GUI 渲染線程與 JavaScript 引擎為互斥」的關(guān)系。

當 JavaScript 引擎執(zhí)行時 GUI 線程會被掛起,GUI 更新會被保存在一個隊列中等到引擎線程空閑時立即被執(zhí)行。

當瀏覽器在執(zhí)行 JavaScript 程序的時候,GUI 渲染線程會被保存在一個隊列中,直到 JS 程序執(zhí)行完成,才會接著執(zhí)行。

因此如果 JS 執(zhí)行的時間過長,這樣就會造成頁面的渲染不連貫,導(dǎo)致頁面渲染加載阻塞的感覺。

另外,如果 JavaScript 文件中沒有操作 DOM 相關(guān)代碼,就可以將該 JavaScript 腳本設(shè)置為異步加載,通過 async 或 defer 來標記代碼。

6. defer 和 async 的區(qū)別 ?

兩者都是異步去加載外部JS文件,不會阻塞DOM解析

Async是在外部JS加載完成后,瀏覽器空閑時,Load事件觸發(fā)前執(zhí)行,標記為async的腳本并不保證按照指定他們的先后順序執(zhí)行,該屬性對于內(nèi)聯(lián)腳本無作用 (即沒有「src」屬性的腳本)。

defer是在JS加載完成后,整個文檔解析完成后,觸發(fā) DOMContentLoaded 事件前執(zhí)行,如果缺少 src 屬性(即內(nèi)嵌腳本),該屬性不應(yīng)被使用,因為這種情況下它不起作用

7. DOMContentLoaded 與 load 的區(qū)別 ?

DOMContentLoaded事件觸發(fā)時:僅當DOM解析完成后,不包括樣式表,圖片等資源。

onload 事件觸發(fā)時,頁面上所有的 DOM,樣式表,腳本,圖片等資源已經(jīng)加載完畢。

那么也就是先DOMContentLoaded -> load,那么在Jquery中,使用(document).load(callback)監(jiān)聽的就是load事件。

那我們可以聊一聊它們與async和defer區(qū)別

帶async的腳本一定會在load事件之前執(zhí)行,可能會在DOMContentLoaded之前或之后執(zhí)行。

情況1:HTML 還沒有被解析完的時候,async腳本已經(jīng)加載完了,那么 HTML 停止解析,去執(zhí)行腳本,腳本執(zhí)行完畢后觸發(fā)DOMContentLoaded事件

情況2:HTML 解析完了之后,async腳本才加載完,然后再執(zhí)行腳本,那么在HTML解析完畢、async腳本還沒加載完的時候就觸發(fā)DOMContentLoaded事件

如果 script 標簽中包含 defer,那么這一塊腳本將不會影響 HTML 文檔的解析,而是等到HTML 解析完成后才會執(zhí)行。而 DOMContentLoaded 只有在 defer 腳本執(zhí)行結(jié)束后才會被觸發(fā)。

情況1:HTML還沒解析完成時,defer腳本已經(jīng)加載完畢,那么defer腳本將等待HTML解析完成后再執(zhí)行。defer腳本執(zhí)行完畢后觸發(fā)DOMContentLoaded事件

情況2:HTML解析完成時,defer腳本還沒加載完畢,那么defer腳本繼續(xù)加載,加載完成后直接執(zhí)行,執(zhí)行完畢后觸發(fā)DOMContentLoaded事件

8. 為什么CSS動畫比JavaScript高效

我覺得這個題目說法上可能就是行不通,不能這么說,如果了解的話,都知道will-change只是一個優(yōu)化的手段,使用JS改變transform也可以享受這個屬性帶來的變化,所以這個說法上有點不妥。

所以圍繞這個問題展開話,更應(yīng)該說建議推薦使用CSS動畫,至于為什么呢,涉及的知識點大概就是重排重繪,合成,這方面的點,我在瀏覽器渲染流程中也提及了。

盡可能的避免重排和重繪,具體是哪些操作呢,如果非要去操作JS實現(xiàn)動畫的話,有哪些優(yōu)化的手段呢?

比如??

使用createDocumentFragment進行批量的 DOM 操作

對于 resize、scroll 等進行防抖/節(jié)流處理。

rAF優(yōu)化等等

剩下的東西就留給你們思考吧,希望我這是拋磚引玉吧(●’?’●)

9. 能不能實現(xiàn)事件防抖和節(jié)流

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

節(jié)流的意思是讓函數(shù)有節(jié)制地執(zhí)行,而不是毫無節(jié)制的觸發(fā)一次就執(zhí)行一次。什么叫有節(jié)制呢?就是在一段時間內(nèi),只執(zhí)行一次。

規(guī)定在一個單位時間內(nèi),只能觸發(fā)一次函數(shù)。如果這個單位時間內(nèi)觸發(fā)多次函數(shù),只有一次生效。

抓取一個關(guān)鍵的點:就是執(zhí)行的時機。要做到控制執(zhí)行的時機,我們可以通過「一個開關(guān)」,與定時器setTimeout結(jié)合完成。

function throttle(fn, delay) {

????let flag = true,

????timer = null;

????return function (…args) {

????????let context = this;

????????if (!flag) return;

????????flag = false;

????????clearTimeout(timer)

????????timer = setTimeout(() => {? ? ? ? ?

????????????fn.apply(context, args);? ? ? ? ? ? ? ? ??

????????????flag = true;? ?

????????}, delay);

????};

}

函數(shù)防抖(debounce)

在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。

核心思想:每次事件觸發(fā)都會刪除原有定時器,建立新的定時器。通俗意思就是反復(fù)觸發(fā)函數(shù),只認最后一次,從最后一次開始計時。

代碼:

function debounce(fn, delay) {

????let timer = null

????return function (…args) {

????????let context = this

????????if(timer) clearTimeout(timer)

????????timer = setTimeout(function() {

????????????fn.apply(context, args)

????????},delay)

????}

}

如何使用 debounce 和 throttle 以及常見的坑

自己造一個 debounce / throttle 的輪子看起來多么誘人,或者隨便找個博文復(fù)制過來。「我是建議直接使用 underscore 或 Lodash」 。如果僅需要 _.debounce 和 _.throttle 方法,可以使用 Lodash 的自定義構(gòu)建工具,生成一個 2KB 的壓縮庫。使用以下的簡單命令即可:

npm i -g lodash-cli

npm i -g lodash-clilodash-cli include=debounce,throttle復(fù)制代碼

常見的坑是,不止一次地調(diào)用 _.debounce 方法:

// 錯誤$(window).on('scroll', function() {

? ?_.debounce(doSomething, 300);?

});// 正確$(window).on('scroll', _.debounce(doSomething, 200));復(fù)制代碼

debounce 方法保存到一個變量以后,就可以用它的私有方法 debounced_version.cancel(),lodash 和 underscore.js 都有效。

let debounced_version = _.debounce(doSomething, 200);

$(window).on('scroll', debounced_version);// 如果需要的話debounced_version.cancel();復(fù)制代碼

適合應(yīng)用場景

防抖

search搜索,用戶不斷輸入值時,用防抖來節(jié)約Ajax請求,也就是輸入框事件。

window觸發(fā)resize時,不斷的調(diào)整瀏覽器窗口大小會不斷的觸發(fā)這個事件,用防抖來讓其只觸發(fā)一次

節(jié)流

鼠標的點擊事件,比如mousedown只觸發(fā)一次

監(jiān)聽滾動事件,比如是否滑到底部自動加載更多,用throttle判斷

比如游戲中發(fā)射子彈的頻率(1秒發(fā)射一顆)

10. 談一談你對requestAnimationFrame(rAF)理解

正好跟節(jié)流有點關(guān)系,有點相似處,就準備梳理一下這個知識點。

「高性能動畫是什么,那它衡量的標準是什么呢?」

動畫幀率可以作為衡量標準,一般來說畫面在 60fps 的幀率下效果比較好。

換算一下就是,每一幀要在 16.7ms (16.7 = 1000/60) 內(nèi)完成渲染。

我們來看看MDN對它的解釋吧??

window.requestAnimationFrame() 方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。該方法使用一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用。— MDN

當我們調(diào)用這個函數(shù)的時候,我們告訴它需要做兩件事:

我們需要新的一幀;

當你渲染新的一幀時需要執(zhí)行我傳給你的回調(diào)函數(shù)

rAF與 setTimeout 相比

rAF(requestAnimationFrame) 最大的優(yōu)勢是「由系統(tǒng)來決定回調(diào)函數(shù)的執(zhí)行時機」。

具體一點講就是,系統(tǒng)每次繪制之前會主動調(diào)用 rAF 中的回調(diào)函數(shù),如果系統(tǒng)繪制率是 60Hz,那么回調(diào)函數(shù)就每16.7ms 被執(zhí)行一次,如果繪制頻率是75Hz,那么這個間隔時間就變成了 1000/75=13.3ms。

換句話說就是,rAF 的執(zhí)行步伐跟著系統(tǒng)的繪制頻率走。它能保證回調(diào)函數(shù)在屏幕每一次的繪制間隔中只被執(zhí)行一次(上一個知識點剛剛梳理完「函數(shù)節(jié)流」),這樣就不會引起丟幀現(xiàn)象,也不會導(dǎo)致動畫出現(xiàn)卡頓的問題。

另外它可以自動調(diào)節(jié)頻率。如果callback工作太多無法在一幀內(nèi)完成會自動降低為30fps。雖然降低了,但總比掉幀好。

與setTimeout動畫對比的話,有以下幾點優(yōu)勢

當頁面隱藏或者最小化時,setTimeout仍然在后臺執(zhí)行動畫,此時頁面不可見或者是不可用狀態(tài),動畫刷新沒有意義,而且浪費CPU。

rAF不一樣,當頁面處理未激活的狀態(tài)時,該頁面的屏幕繪制任務(wù)也會被系統(tǒng)暫停,因此跟著系統(tǒng)步伐走的rAF也會停止渲染,當頁面被激活時,動畫就從上次停留的地方繼續(xù)執(zhí)行,有效節(jié)省了 CPU 開銷。

什么時候調(diào)用呢

規(guī)范中似乎是這么去定義的:

在重新渲染前調(diào)用。

很可能在宏任務(wù)之后不去調(diào)用

這樣子分析的話,似乎很合理嘛,為什么要在重新渲染前去調(diào)用呢?因為rAF作為官方推薦的一種做流暢動畫所應(yīng)該使用的API,做動畫不可避免的去操作DOM,而如果是在渲染后去修改DOM的話,那就只能等到下一輪渲染機會的時候才能去繪制出來了,這樣子似乎不合理。

rAF在瀏覽器決定渲染之前給你最后一個機會去改變 DOM 屬性,然后很快在接下來的繪制中幫你呈現(xiàn)出來,所以這是做流暢動畫的不二選擇。

至于宏任務(wù),微任務(wù),這可以說起來就要展開篇幅了,暫時不在這里梳理了。

rAF與節(jié)流相比

跟 _.throttle(dosomething, 16) 等價。它是高保真的,如果追求更好的精確度的話,可以用瀏覽器原生的 API 。

可以使用 rAF API 替換 throttle 方法,考慮一下優(yōu)缺點:

優(yōu)點

動畫保持 60fps(每一幀 16 ms),瀏覽器內(nèi)部決定渲染的最佳時機

簡潔標準的 API,后期維護成本低

缺點

動畫的開始/取消需要開發(fā)者自己控制,不像 ‘.debounce’ 或 ‘.throttle’由函數(shù)內(nèi)部處理。

瀏覽器標簽未激活時,一切都不會執(zhí)行。

盡管所有的現(xiàn)代瀏覽器都支持 rAF ,IE9,Opera Mini 和 老的 Android 還是需要打補丁。

Node.js 不支持,無法在服務(wù)器端用于文件系統(tǒng)事件。

根據(jù)經(jīng)驗,如果 JavaScript 方法需要繪制或者直接改變屬性,我會選擇 requestAnimationFrame,只要涉及到重新計算元素位置,就可以使用它。

涉及到 AJAX 請求,添加/移除 class (可以觸發(fā) CSS 動畫),我會選擇 _.debounce 或者 _.throttle ,可以設(shè)置更低的執(zhí)行頻率(例子中的200ms 換成16ms)。

002.jpg

云和數(shù)據(jù)HTML5全棧精英班,經(jīng)過多年的技術(shù)迭代和項目革新,逐步發(fā)展成為集網(wǎng)站、手機應(yīng)用、小程序、快應(yīng)用、桌面應(yīng)用、后臺開發(fā)等多領(lǐng)域開發(fā)課程,新增Egg、TypeScript、Vue、React、HybridAPP等時下最流行的新技術(shù),結(jié)合企業(yè)實際用人需求,只為培養(yǎng)更多高端IT技術(shù)人才。

聲明:除云和數(shù)據(jù)原創(chuàng)文章外,分享和轉(zhuǎn)載的文章皆為促進IT技術(shù)的傳播,并不代表本微信贊同其觀點和對真實性負責(zé),僅做交流學(xué)習(xí)使用,非商業(yè)用途。如有文章或圖片的原作者有異議或涉及版權(quán)問題,請立即聯(lián)系我們,我們將在第一時間進行改正或刪除,確保您的權(quán)益,謝謝支持!

相關(guān)內(nèi)容

搶先一步 鴻蒙(HarmonyOS)應(yīng)用開發(fā)者高級認證 免費考! 適合人群計算機相關(guān)專業(yè)在校生(技師、中職、高職、本科、研究生)對鴻蒙(HarmonyOS)有興趣的非計算機相關(guān)專業(yè)在校生目前正在從事移動應(yīng)用的開發(fā)者目前正在從事計算機行業(yè)相關(guān)的人計算機專業(yè)高校老師所有對鴻蒙(HarmonyOS)有興趣的人 培訓(xùn)方案掌握鴻蒙的核心概念和端云一體化開發(fā)、... 什么是Java的多態(tài)性(polymorphism)?它有哪些不同的形式? 多態(tài)性是Java面向?qū)ο缶幊痰囊粋€重要概念,它允許不同的對象以一致的方式響應(yīng)同一個方法調(diào)用,具體表現(xiàn)為對象在運行時可以表現(xiàn)出多個不同的形態(tài)。多態(tài)性主要有兩種不同的形式:編譯時多態(tài)性(靜態(tài)多態(tài)性)和運行時多態(tài)性(動態(tài)多態(tài)性)。1. 編譯時多態(tài)性(靜態(tài)多態(tài)性):   ... 如何學(xué)習(xí)和搭建Hadoop開發(fā)環(huán)境? Hadoop是大數(shù)據(jù)處理領(lǐng)域的重要平臺,能夠處理和分析大量數(shù)據(jù)。為了有效地利用Hadoop,我們需要學(xué)習(xí)其基礎(chǔ)知識,并正確搭建開發(fā)環(huán)境。下面是詳細的學(xué)習(xí)和搭建指南。一、學(xué)習(xí)Hadoop基礎(chǔ)掌握基礎(chǔ)概念和原理Hadoop主要由HDFS和MapReduce兩部分組成。HDFS是分布式文件系統(tǒng),Ma... UI 設(shè)計學(xué)習(xí)如何進階成為高手 我總結(jié)了六種方法,幫助你走出舒適區(qū),提高技能,成長為自信且經(jīng)驗豐富的UI設(shè)計高手一位經(jīng)驗豐富的 UI 設(shè)計師,往往十分看中應(yīng)用程序界面的吸引力和視覺刺激,確保滿足用戶期望和需求。但是,如果你已經(jīng)在 UI 設(shè)計圈摸爬滾打多年,仍然沒有出色的作品,那你極有可能是因為陷入了一個舒適圈,UI技能一直原... 在Java中Executor和Executors的區(qū)別? 在Java中,Executor和Executors都與線程池和并發(fā)執(zhí)行有關(guān),但它們是不同的概念和類。1.ExecutorExecutor是一個接口,位于java.util.concurrent包中,用于表示一個執(zhí)行任務(wù)的執(zhí)行器。它只定義了一個方法:void execute(Runnable c... String類型的常見命令有哪些? String類型,也就是字符串類型,是Redis中最簡單的存儲類型。其value是字符串,不過根據(jù)字符串的格式不同,又可以分為3類:string是普通字符串,int整數(shù)類型,可以做自增、自減操作,float浮點類型,可以做自增、自減操作。String的常見命令有:SET:添加或者修改已經(jīng)存在的...