摘要:本文總結了前端老司機經常問題的一些問題并結合個人總結給出了比較詳盡的答案。網易阿里騰訊校招社招必備知識點。此外還有網絡線程,定時器任務線程,文件系統處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機制叫做事件循環。
本文總結了前端老司機經常問題的一些問題并結合個人總結給出了比較詳盡的答案。網易阿里騰訊校招社招必備知識點。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-你不知道的那些細節
課程知識在不斷更新,本片內容也逐步更新
官方博客:FED123前端學堂
瀏覽器是按照文檔流解析html,為了更快構建DOM樹和渲染樹將頁面呈現到屏幕上,建議是降js放在文檔dom樹結尾,body標簽閉合前。
瀏覽器并發HTTP請求有限制(6個左右),加載頁面html后開始解析,解析到外鏈資源比如js css和圖片,就會發http請求獲取對應資源。減少請求就是減少這些資源請求, 可以 css資源合并,js資源合并,圖片資源合并同時做lazyload,區分首屏非首屏接口,按需請求數據。
雪碧圖是一種圖片資源的合并方法,將一些小圖片合成一張圖,通過background-position來定位到對應部分。
window.performance.timing 參考下前端頁面性能指標數據計算方法, performance接口屬于w3c標準hight resolution time中的一部分,通過navigation timeline api 、 performance timeline api,user timing api,resource timeline api 這四個接口做了增強實現。其中navigation timeline api中PerformanceTiming?接口數據放在?performance.timing這個對象上。主要記錄了瀏覽器從跳轉開始的各個時間點的時間,比如navigationStart是頁面開始跳轉時間,fetchStart是頁面開始時間,domainLookupStart是DNS開始時間,domainLookupEnd是DNS結束時間, 查找到DNS后建立http鏈接,connectStart和connectEnd分別是鏈接開始和結束時間,然后是requestStart開始發起請求時間,responseStart開始響應時間,responseEnd響應結束時間。然后是茍安DOM樹時間,分別是domLoading, domInteractive, domContentLoad和domComplete時間,分別對應document.readyState狀態loading、interactive和complete。最后是頁面onload,分別是loadEventStart和loadEventEnd時間節點。
可以通過這個接口統計前端的頁面性能數據。
domainLookupStart -?fetchStart = appCache時間,這段時間瀏覽器首先檢查緩存
domainLookupEnd -domainLookupStart = DNS時間
connectEnd - connectStart = TCP時間
responseStart - requestStart = FTTB首字節時間,或者說是服務器響應等待時間
domContentLoad -?navigationStart = 頁面pageLoad時間
?loadEventEnd -?navigationStart = 頁面onLoad時間
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
2.請你描述下一個網頁是如何渲染出來的,dom樹和css樹是如何合并的,瀏覽器的運行機制是什么,什么是否會造成渲染阻塞?參考下:瀏覽器工作原理???瀏覽器渲染與阻塞原理
第一部分通過performance.time這個api我們可以了解瀏覽器加載網頁的流程,瀏覽器邊加載html邊構建DOM樹,當然會有容錯和修正機制。瀏覽器解析到行內css和內聯css會立馬加入到構建渲染樹,解析到外鏈css就開始加載,加載完之后也會合并到渲染樹的構建中,最后將渲染樹和DOM做節點鏈路匹配,也叫layout階段,計算每個DOM元素最終在屏幕上顯示的大小和位置。 遍歷順序為從左至右,從上到下,繪制在屏幕上,layout過程中可能會觸發頁面回流和重繪,比如某個外鏈css加載完解析之后合并構建到渲染樹中,其中某個css改變了DOM樹種某個元素的定位(改成絕對定位)或者改變了長寬邊距等位置信息,會觸發重新layout,所以會回流reflow。重繪是比如css改變了之前的背景圖片顏色,瀏覽器會重新繪制。
會有渲染阻塞,瀏覽器刷新的頻率大概是60次/秒, 也就是說刷新一次大概時間為16ms,如果瀏覽器對每一幀的渲染工作超過了這個時間, 頁面的渲染就會出現卡頓的現象。瀏覽器內核中有3個最主要的線程:JS線程,UI渲染線程,事件處理線程。此外還有http網絡線程,定時器任務線程,文件系統處理線程等等。
JS線程負責JS代碼解析編譯執行,稱為主線程。常說‘瀏覽器是單線程’指的是JS主線程只能有一個,主線程執行同步任務,會阻塞UI渲染線程。JS線程核心是js引擎 (IE9+: Chakra firefox:monkey chrome:v8)。webworker可以創建多個js線程,但是受主線程控制,主要用于cpu密集型計算。
UI渲染線程當然是負責構建渲染樹,執行頁面元素渲染。核心是渲染引擎(firefox:gecko、chrome/safari:webkit),由于JS可以操作DOM元素處理樣式等,JS主線程是執行同步任務的,所以設計上JS引擎線程和GUI渲染線程是互斥的。 也就是說JS引擎處于運行狀態時,GUI渲染線程將處于凍結狀態。
事件處理線程,由于瀏覽器是事件驅動的,事件處理線程用來控制事件回調處理,瀏覽器觸發某個事件后會把事件回調函數放到任務隊列中,可以看下下面會提到。
其他線程統稱工作線程,如處理 ajax 的線程,dom事件線程、定時器線程、讀寫文件的線程等,工作線程的任務完成之后, 會推入到一個任務隊列(task queue)
總結一下,渲染阻塞有兩個方面:
js主線程執行時間長會導致渲染線程阻塞,影響渲染。我們也稱為longtask
渲染線程自身阻塞,渲染時間達不到幀率60,會看起來卡頓,比如回流或者重繪等,或者css效率太低,動畫處理不合適,導致渲染耗時
3.請簡述下js引擎的工作原理,js是怎樣處理事件的eventloop,宏任務源tasks和微任務源jobs分別有哪些?js是如何構造抽象語法樹(AST)的?
js引擎只執行同步任務, 異步任務會有工作線程來執行,當需要進行異步操作(定時器、ajax請求、dom事件注冊等), 主線程會發一個異步任務的請求, 相應的工作線程接受請求; 當工作線程完成工作之后, 通知主線程;主線程接收到通知之后, 會執行一定的操作(回調函數)。主線程和工作線程之間的通知機制叫做事件循環。
調用棧 (call stack): 主線程執行時生成的調用棧
任務隊列 (task queue): 工作線程完成任務后會把消息推到一個任務隊列, 消息就是注冊時的回調函數
當調用棧為空時, 主線程會從任務隊列里取一條消息并放入當前的調用棧當中執行, 主線程會一直重復這個動作直到消息隊列為空。 這個過程就叫做事件循環 (event-loop)。
關于宏任務和微任務,參考?事件流、事件模型、事件循環概念理解??瀏覽器線程理解與microtask與macrotask
ES6新引入了Promise標準,同時瀏覽器實現上多了一個microtask微任務概念。在ECMAScript中,microtask稱為jobs,macrotask可稱為task。
macrotask宏任務tasks,也就是上面說到的任務隊列的任務。執行棧上的每個任務都屬于宏任務,主線程執行完執行棧的任務,從任務隊列取新的任務。宏任務執行時不會中斷,會一次性執行完,為了及時渲染數據,主線程執行完一個宏任務之后,會執行一次渲染。
task--》渲染 --》宏任務 --》渲染? .....
microtask微任務jobs,可以看成是插隊需要及時處理的任務,會在當前主線程task任務執行后,渲染線程渲染之前,執行完當前積累所有的微任務。
task--》jobs --》渲染 --》宏任務 --》jobs --》渲染? .....
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
AST 參考:程序語言進階之DSL與AST實戰解析
將抽象語法樹之前要先了解下NLP中文法的概率。任何一種語言,具體說就是DSL,都有自己的一套文法,用來表示這套語言的邏輯規范。不同的文法寫出來的語法表達式也不一樣。我們根據語法表達式來解析語言,就可以形成一個AST抽象語法樹。然后可以作進一步處理。我常用的是PEG解析表達式語法。可以很輕松的寫出語法的每一條產生式規則,來構造生成AST。所謂AST可以理解成按照一定語法結構組成的詞匯流,每個詞匯有特定的語法含義,比如說這是一個聲明,這個一個操作符等等。
上面這個圖是蘋果最早做的KHTML渲染引擎中的KJS(javascript引擎),他是基于AST來實現的JavaScript語言解析的,先通過詞法分析得到JSTokens流,然后經過語法分析得到抽象語法樹,然后經過字節碼生成器,轉換成字節碼。字節碼經過JavaScript虛擬機JIT編譯成機器碼,然后執行。這是最初的設計架構,后來蘋果公司基于此重構出了webkit渲染引擎,google基于webkit多帶帶維護,稱為blink渲染引擎,chrome的JS引擎改造為V8引擎。參考:簡述Chromium, CEF, Webkit, JavaScriptCore, V8, Blink
舉個例子常用的babel插件的原理就是基于babylon詞法語法分析器生成抽象語法樹,將代碼文本轉換成按照特定語法組合的token流集合,然后經過babtlon-traverse這個組件來負責處理遍歷語法樹,訪問每個token節點,通過對token的處理,可以生成我們需要的AST語法樹,然后再通過babylon-generator這個組件來做代碼生成,根據AST生成代碼。比如可以將 箭頭函數 轉換成 function函數。
瀏覽器中,通過開發者調試工具分析就能看到,下載完js腳本后,首先瀏覽器要先解析代碼=》初始化上下文環境=》執行代碼,整個是evaluate script的過程,解析代碼的過程也是編譯js的過程所以看最前面第一步就是compile script,將js代碼編譯成字節碼(這一塊涉及到瀏覽器js引擎的優化,v8引擎是編譯成字節碼,后面經過JIT解析執行(這個參考 你不知道的LLVM編譯器?可以提升效率做動態優化), 這個類似于java、C#這些需要將源代碼編譯成中間語言,然后在虛擬機執行,javascript編譯成字節碼后面也是在虛擬機執行),然后就開始執行腳本。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
4.你是否考慮全面你編寫的整個函數,或者整個功能的容錯性與擴展性?怎樣構建一個組件是最合理最科學的,對于錯誤的處理是否有統一的方式方法?擴展性主要是從功能上考慮,容錯性是從數據上考慮。
設計開發組件的時候首先要設計好數據模型,當然可以和后端共同約定一個標準,后面只要是這部分都用這個標準字段。后面可以對標準字段做擴展,開發時候要做容錯和數據響應式開發。
功能這部分其實可以從基礎功能和擴展功能來看,基礎功能可以在原有組件上做根據數據來展示。擴展功能可以通過組件結合的形式來處理。
我主要考慮的是組件復用,可以將一類組件歸類,比如商品卡片,基本都是頭圖加標題行動點,價格,按鈕。這就是最基礎的一個組件。擴展性可以通過數據來做響應式的展示,比如新增一個描述,數據模型新增描述字段,有描述字段卡片上就展示描述,沒有就不展示。像點擊按鈕的加購功能可以多帶帶做成功能組件,統一處理,而不放在卡片上。因為這種加購往往附帶的是商業邏輯,有很多業務邏輯要處理,獨立出來反而更利于維護和拓展。
錯誤處理我們這邊是基于組件的方式來處理,開發一個錯誤處理的功能組件,提供thenable的能力,區分不同的錯誤類型,提供統一埋點做監控和記錄。
5.瀏覽器緩存的基本策略,什么時候該緩存什么時候不該緩存,以及對于控制緩存的字段的相關設置是否清楚?參考下:HTTP協商緩存VS強緩存原理
前面介紹navigation api時候介紹了瀏覽器加載頁面的各個關鍵時間節點。和緩存相關的主要有兩部分
appcache,這部分是離線緩存,在fetchStart和domainLookupStart之間,這部分參考whatwg標準已經棄用,建議用serviceworker。這里也不做介紹。
HTTP緩存這部分是在requestStart開始,發起資源http請求開始,這部分涉及到強緩存和協商緩存。瀏覽器對于請求過得資源會緩存下來請求的響應數據,后面請求時會先從緩存查找匹配的請求的響應頭,如果命中強緩存(判斷cache-control和expires信息)那么直接從緩存獲取響應數據,不會再發送http請求。如果沒有命中瀏覽器會發送請求到服務器,同時會攜帶第一次請求的響應頭的緩存相關header字段(last-modified/if-modified-since, Etag/if-none-match), 服務端根據這些請求頭判斷是否走緩存,如果走緩存,服務端會返回新的響應頭,但不返回數據,瀏覽器會更新響應頭,從緩存拿數據。如果不走緩存,服務端就會返回新的響應頭和數據,然后瀏覽器更新緩存的數據。
-》強緩存,判斷依據是expires(http 1.0協議規定)和cache-control(http 1.1協議規定)字段,expires是絕對時間,cache-control有可選值no-cache(不使用本地緩存,走協商緩存),no-store(禁止瀏覽器緩存數據,每次都是重新獲取數據),public(可以被客戶端和中間商CDN做緩存),private(只能客戶端緩存,CDN不能緩存)
-》協商緩存,用到的響應頭字段是last-modified/if-modified-since, Etag/if-none-match,這是兩對哈,每隊/前面一個是服務端返回的response header中的字段,/后面是請求頭request攜帶的頭部字段,第一次請求資源瀏覽器會返回last-modified(最后修改時間),后面再次請求請求頭會帶上if-modified-since,當然這個值和上次瀏覽器返回的last-modified是一樣的,然后瀏覽器判斷如果文件沒有變化,那么返回304?Not Modified http code,響應請求頭不會攜帶last-modified字段,瀏覽器從緩存取數據,也不用更新last-modified字段,如果有修改,那么響應頭返回新的last-modified字段數據,返回響應內容。Etag/if-none-match這一對是同樣的邏輯,不同之處是用etag標識來判斷文件是否修改,而不是用時間,因為服務器時間可能會變的,還會收到時區的影響。還有一點是每次請求都會返回etag字段,即使沒有變化。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
6.你是否可以利用面向對象的思維去抽象你的功能,你會構建一個class(ES6)嗎?你對于前端架構的理解?我目前開發分情況用不同的技術框架。
如果單純開發導購頁面,比如一個商品列表頁面,這種為了加載性能和操作體驗,我是不考慮用框架的,也不用class,單純用自己開發的原生ES框架自己控制頁面模塊生命周期,基于函數式編程寫stateless組件。盡量減少復雜度,簡單化。
如果是開發功能性組件,我是會用面型對象的模式來做開發。面向對象的核心是封裝、繼承、多態。封裝就是將具體化為抽象,抽象成class,封裝抽象出來的屬性和方法。繼承是因為抽象可以有層級,比如對異常處理,參數異常可以抽象成一類,狀態異常可以抽象成一類,參數異常和狀態異常有共通的地方,比如結構上都會返回異常的名稱和描述,這就可以抽象一層公共父類,然后這兩個異常繼承自公共父類,這就是集成。多態也是隨著繼承而來的,比如參數異常和狀態異常都繼承了name這個屬性,都可以實現對應的get方法,但是他們的實現結果可定是不一樣的,根據自身類的抽象來實現,調用的時候調用同樣的方法也就有不同的表現。比如參數異常和狀態異常都繼承了toString的方法,在調用各自的實例的toString方法時,輸出的數據是不一樣的。另外設計的2大原則是:單一職責原則和開放封閉原則。單一職責只是抽象的類盡量保持功能專一,開閉原則指設計的時候要考慮好擴展,對修改關閉,對擴展開放。
export class RuntimeException { constructor(message) { this._message = message; } get name() { return "RuntimeException"; } get message() { return this._message; } toString() { return this.name + ": " + this.message; } } export class IllegalStateException extends RuntimeException { constructor(message) { super(message); } get name() { return "IllegalStateException"; } } export class InvalidArgumentException extends RuntimeException { constructor(message) { super(message); } get name() { return "InvalidArgumentException"; } } export class NotImplementedException extends RuntimeException { constructor(message) { super(message); } get name() { return "NotImplementedException"; } }
對于前端領域來說,目前前端框架做掉了很多事情,搭建好項目框架之后,開發的就行就是填功能。所編寫的模塊和組件的模式也比較固定,可以根據具體情況來實現。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
7.你會用VUE,你會用React,你讀得懂這兩個架構的源碼嗎?你懂他倆的基本設計模式嗎?讓你去構建一個類似的框架你如何下手?angular
特點: 數據雙向綁定-》數據驅動開發的思想
html標簽化的模板,模塊化思想
數據綁定,控制器,依賴注入,
服務,指令,過濾器…
優點: 比較完善規范,文檔、社區比較活躍
模塊清晰,代碼明了
缺點: 功能規范太固定,開發發揮空間小。
相對react和vue,不夠輕量化
擴展性不夠靈活
react
特點: 強大的組件化思想,任意封裝、組合
獨創JSX語法,virtual dom智能patch,靈活高效
輕量,易擴展,模塊清晰,代碼明了
社區生態完善,組件庫、插件庫豐富
新特性: hooks,錯誤邊界等優化
缺點: 組件難以在復雜交互場景復用
側重于做組件,做view展示層,對于業務邏輯等封裝治理不如angular強大
JSX中html模板不夠完備和健壯,比如一些屬性變換寫法,綁定事件大小寫
vue
特點: 文檔豐富,容易上手
模板較完備,聲明式渲染,插值表達式與指令系統,
事件處理器,修飾符,計算屬性?,簡單易用,功能強
社區生態完善,組件庫、插件庫豐富
缺點: 輕量框架使用是要結合生態插件組件使用,項目初始配置比較麻煩,
不過可以參考各種場景的標準模板配置,很多腳手架
聲明式渲染與命令式渲染:? 這個涉及到函數式編程中的一個聲明式編程和命令式編程的概念。
比如命令式編程:
let a = [] for(let i=0; i< 10; i++){ a.push(i*10) }
聲明式編程:
let a = [] arr.forEach(i=>{ a.push(i*10) })
聲明式編程隱藏了函數處理細節,命令式編程則需要處理細節。
聲明式編程的好處是簡單化,易于理解,減少勞動量。比如vue中的指令綁定事件,綁定屬性都是這樣。@click,:title等等,用的時候很方便,這正是聲明式編程最直觀的好處。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
8.你了解的ES6只是const、let、promise嗎?你考慮過ES6提出的真正趨勢嗎?怎么可能,我又不是你。ES6中最常用的像變量定義這部分用let、const可以避免一些坑,異步處理可以用promise,不過我到喜歡用async/await 更簡潔好用。
還有簡寫的箭頭函數,代碼看起來更清晰。
"..." 變量析構和組裝, 函數默認值
``模板字符串,便于字符拼接;標簽模板 功能
Object對象的擴展, for in, Object.keys
Set和Map
class和module相關
發展趨勢: 總體來說前端開發更規范,更簡單,語法更完備和成熟。支持的功能增強,開發效率提升,體驗增強。
ES6的模塊化相關支持,可以更好地支持模塊化開發。
原生支持class,面向對象的編程,概念更容易理解,易于軟件開發和集成。
異步操作規范化,異步編程更簡單
參考下:程序語言進階之DSL與AST實戰解析
可以的,說一下原理,需要將 .less 文件最終解析成CSS,less是一種DSL,我們可以現根據less預發,先將其解析成AST,然后解析成CSS即可。 我推薦用PEG.js這種解析表達式語法更簡單些,只需要描述產生式規則即可。也可以自己根據LESS預發來寫正則表達來匹配規則,然后轉換成css。比較常用的是PostCSS,處理流程如下:參考官方文檔
PostCSS的處理流程也是經過詞法解析語法分析,將讀取到的文件字符轉化成詞匯流tokens,根據語法分析,根據less的語法,解析成一個AST。
source string → tokens → AST
核心組件有:
詞法分析器 Tokenizer (?lib/tokenize.es6?)
語法分析器 Parser (?lib/parse.es6,?lib/parser.es6?)
插件處理器 Processor (?lib/processor.es6?)
代碼生成器 Stringifier (?lib/stringify.es6,?lib/stringifier.es6?)
大家之前應該用過gulp,grunt這種代碼打包工具,定義不同的打包任務和打包流程。我用的比較多的rollup這個打包工具,配置起來比較簡單些。
webpack也是用來做代碼打包,可以做代碼分析,拆分,混淆,壓縮等等,基于他的插件擴展機制可以做很多事情。分析webpack的原理,可以先從webpack配置文件說起。參考:webpack編譯代碼原理介紹?用webpack4和一些插件提升代碼編譯速度
首先作為打包工具,要定義打包的輸入entry和輸出output;然后是定義webpack要用到的module,比如babel js loader, cssloader等等。執行編譯具體的流程是:
加載webpack配置文件 --》 根據配置初始化編譯器compiler --》找到入口,根據loader配置開始編譯入口文件以及層層依賴 --》編譯完成之后,可以得到所有編譯過的文件和依賴關系結構 --》根據依賴關系將模塊組裝成一個個包含多個模塊的chunk,然后根據配置寫到輸出文件。
webpack構建流程可分為以下三大階段。
初始化:啟動構建,讀取與合并配置參數,加載plugin,實例化Compiler
編譯:從Entry出發,針對每個Module串行調用對應的Loader去翻譯文件中的內容,再找到該Module依賴的Module,遞歸的進行編譯處理
輸出:將編譯后的Module組合成Chunk,將Chunk轉換成文件,輸出到文件系統中
分析依賴是在編譯過程中完成的,從入口查找依賴,最后形成依賴關系。 為了提高效率,可以記錄分析過的依賴,這樣下次遇到同樣的模塊就不用再分析,直接引用編譯過的依賴就可以了。
tree-shaking的名字原理一樣,就是搖一搖大樹,落下來的葉子都是冗余的部分。Tree-shaking 較早由 Rich_Harris 的 rollup 實現,后來,webpack2 也增加了tree-shaking 的功能。其實在更早,google closure compiler 也做過類似的事情。三個工具的效果和使用各不相同,使用方法可以通過官網文檔去了解。
tree shaking的目的是去掉無用代碼,減少代碼體積。其實對于編譯的編程語言對應的編譯器基本都有判斷哪些代碼不會影響輸出,從而在編譯時移除這些代碼的功能,稱為DCE(dead code elimination)。tree shaking 是DCE的一種實現,傳統的是消除沒有引用不會執行的代碼,tree shaking 主要是要消除沒有用的代碼。
Dead Code 一般具有以下幾個特征
?代碼不會被執行,不可到達
?代碼執行的結果不會被用到
?代碼只會影響死變量(只寫不讀)
在前端代碼打包處理中,最終都會有個代碼壓縮混淆的環節,這個環節其實會完成DCE的工作,會將這些dead code移除。
但是uglify代碼是只是單個單個文件處理,并不能分析出這個代碼有沒有被其他文件用到,當然也不會對這些為被調用的函數做處理,如上圖uglify就不會去除沒用到的get函數,所以就需要tree shaking。tree shaking是有限制的,只能消除函數和import/export的變量,不會處理import/export的class(因為javascript動態語言特性使得分析比較困難,可能導致以外的錯誤,side effect比較大), 對于純函數處理效果較好。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
具體參考https://github.com/wintercn/b...
static:無特殊定位,對象遵循正常文檔流。top,right,bottom,left等屬性不會被應用。
relative:對象遵循正常文檔流,但將依據top,right,bottom,left等屬性在正常文檔流中偏移位置,相對定位相對的是它原本在文檔流中的位置而進行的偏移。而其層疊通過z-index屬性定義。占據的文檔空間不會隨 top / right / left / bottom 等屬性的偏移而發生變動,也就是說它后面的元素是依據( top / left / right / bottom 等屬性生效之前)進行的定位,這點一定要理解。
absolute:對象脫離正常文檔流,使用top,right,bottom,left等屬性進行絕對定位。而其層疊通過z-index屬性定義。使用absoulte或fixed定位的話,必須指定 left、right、 top、 bottom 屬性中的至少一個,否則left/right/top/bottom屬性會使用它們的默認值 auto ,這將導致對象遵從正常的HTML布局規則,在前一個對象之后立即被呈遞,簡單講就是都變成relative,會占用文檔空間,這點非常重要,很多人使用absolute定位后發現沒有脫離文檔流就是這個原因。
fixed:對象脫離正常文檔流,使用top,right,bottom,left等屬性以窗口為參考點進行定位,當出現滾動條時,對象不會隨著滾動。而其層疊通過z-index屬性定義。
sticky:?The element is positioned according to the normal flow of the document, and then offset relative to its?nearest scrolling ancestor?and?containing block?(nearest block-level ancestor), including table-related elements, based on the values of?top,?right,?bottom, and?left. The offset does not affect the position of any other elements.This value always creates a new?stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when?overflow?is?hidden,?scroll,?auto, or?overlay), even if that ancestor isn"t the nearest actually scrolling ancestor. This effectively inhibits any "sticky" behavior (see the?Github issue on W3C CSSWG).
absolute就只能根據祖先類元素(父類以上)進行定位,而這個祖先類還必須是以postion非static方式定位的, 舉個例子,a元素使用absoulte定位,它會從父類開始找起,尋找以position非static方式定位的祖先類元素(注意,一定要是直系祖先才算哦~),直到標簽為止,這里還需要注意的是,relative和static方式在最外層時是以
標簽為定位原點的,而absoulte方式在無父級是position非static定位時是以作為原點定位。參考:?position屬性
關于Layout and the containing block,看下官方介紹的contain block,另外相關的點是 BFC:如何創建塊級格式化上下文(block formatting context),BFC有什么用
12 前端動畫渲染機制了解嗎?硬件加速原理?參考:瀏覽器渲染流水線解析與網頁動畫性能優化
動畫可以看做是一個連續的幀序列的組合。我們把網頁的動畫分成兩大類 —— 一類是合成器動畫,一類是非合成器動畫(UC 內部也將其稱為內核動畫或者 Blink Animation,雖然這不是 Chrome 官方的術語)。
合成器動畫顧名思義,動畫的每一幀都是由 Layer Compositor 生成并輸出的,合成器自身驅動著整個動畫的運行,在動畫的過程中,不需要新的 Main Frame 輸入;
非合成器動畫,每一幀都是由 Blink 生成,都需要產生一個新的 Main Frame;
合成器動畫又可以分為兩類:
合成器本身觸發并運行的,比如最常見的網頁慣性滾動,包括整個網頁或者某個頁內可滾動元素的滾動;
Blink 觸發然后交由合成器運行,比如說傳統的 CSS Translation 或者新的 Animation API,如果它們觸發的動畫經由 Blink 判斷可以交由合成器運行;
Blink 觸發的動畫,如果是 Transform 和 Opacity 屬性的動畫基本上都可以由合成器運行,因為它們沒有改變圖層的內容。不過即使可以交由合成器運行,它們也需要產生一個新的 Main Frame 提交給合成器來觸發這個動畫,如果這個 Main Frame 包含了大量的圖層變更,也會導致觸發的瞬間卡頓,頁端事先對圖層結構進行優化可以避免這個問題。
非合成器動畫也可以分為兩類:
使用 CSS Translation 或者 Animation API 創建的動畫,但是無法由合成器運行;
使用 Timer 或者 rAF 由 JS 驅動的動畫,比較典型的就是 Canvas/WebGL 游戲,這種動畫實際上是由頁端自己定義的,瀏覽器本身并沒有對應的動畫的概念,也就是說瀏覽器本身是不知道這個動畫什么時候開始,是否正在運行,什么時候結束,這些完全是頁端自己的內部邏輯;
合成器動畫和非合成器動畫在渲染流水線上有較大的差異,后者更復雜,流水線更長。上面四種動畫的分類,按渲染流水線的復雜程度和理論性能排列(復雜程度由低到高,理論性能由高到低):
合成器本身觸發并運行的動畫;
Blink 觸發,合成器運行的動畫;
Blink 觸發,無法由合成器運行的動畫;
由 Timer/rAF 驅動的 JS 動畫;
開啟硬件加速的方法很多,比如transform:?translate3d(0,0,0); 加了之后,在chrome開發者工具中的layer欄目下可以看到多了一層 composition layer,同時給出了理由描述是開啟了3D transform,這個元素就放入了Composited Layer中托管,其動畫效果都是在多帶帶一個圖形層上面處理,不會影響其它層。
什么情況下能使元素獲得自己的層?雖然 Chrome 的啟發式方法(heuristic)隨著時間在不斷發展進步,但是從目前來說,滿足以下任意情況便會創建層:
3D 或透視變換(perspective transform) CSS 屬性
使用加速視頻解碼的 元素
擁有 3D (WebGL) 上下文或加速的 2D 上下文的 元素
混合插件(如 Flash)
對自己的 opacity 做 CSS 動畫或使用一個動畫 webkit 變換的元素
擁有加速 CSS 過濾器的元素
元素有一個包含復合層的后代節點(換句話說,就是一個元素擁有一個子元素,該子元素在自己的層里)
元素有一個 z-index 較低且包含一個復合層的兄弟元素(換句話說就是該元素在復合層上面渲染)
使用3D硬件加速提升動畫性能時,最好給元素增加一個z-index屬性,人為干擾復合層的排序,可以有效減少chrome創建不必要的復合層,提升渲染性能,移動端優化效果尤為明顯。
關于層的介紹:gpu-accelerated-compositing-in-chrome
理解CSS animations 和 transitions的性能問題與動畫調試
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
13.你了解js的數據結構嗎?基本數據類型有哪些?復雜數據類型有哪些?在內存是如何表現的?參考MDN,最新的 ECMAScript 標準定義了 7 種數據類型:
6 種原始類型:
Boolean
Null
Undefined
Number
String
Symbol?(ECMAScript 6 新定義)
和?Object
除 Object 以外的所有類型都是不可變的(值本身無法被改變)。例如,與 C 語言不同,JavaScript 中字符串是不可變的。JavaScript 中對字符串的操作一定返回了一個新字符串,原始字符串并沒有被改變。
標準的" 對象, 和函數【復雜數據類型】
日期:內建的?Date?對象
數組和類型數組:
數組是一種使用整數作為鍵(integer-key-ed)屬性和長度(length)屬性之間關聯的常規對象。此外,數組對象還繼承了 Array.prototype 的一些操作數組的便捷方法。例如,?indexOf?(搜索數組中的一個值) or?push?(向數組中添加一個元素),等等。?這使得數組是表示列表或集合的最優選擇。
類型數組(Typed Arrays)是ECMAScript Edition 6中新定義的 JavaScript 內建對象,提供了一個基本的二進制數據緩沖區的類數組視圖。
集合對象Map、WeakMap、Set、WeakSet:這些數據結構把對象的引用當作鍵,其在ECMAScript第6版中有介紹。當?Map?和?WeakMap?把一個值和對象關聯起來的時候,?Set?和?WeakSet?表示一組對象。 Map和WeakMaps之間的差別在于,在前者中,對象鍵是可枚舉的。
結構化數據JSON:JSON (JavaScript Object Notation) 是一種輕量級的數據交換格式
參考:標準全局內置對象
兩種類型:
1.???ECMAScript變量包含兩種不同類型的值:基本類型值、引用類型值;
2.???基本類型值:指的是保存在棧內存中的簡單數據段;
3.???引用類型值:指的是那些保存在堆內存中的對象,意思是,變量中保存的實際上只是一個指針,這個指針執行內存中的另一個位置,由該位置保存對象;
兩種訪問方式:
4.???基本類型值:按值訪問,操作的是他們實際保存的值;
5.???引用類型值:按引用訪問,當查詢時,我們需要先從棧中讀取內存地址,然后再順藤摸瓜地找到保存在堆內存中的值;
數據復制
基本類型變量的復制:從一個變量向一個變量復制時,會在棧中創建一個新值,然后把值復制到為新變量分配的位置上;
引用類型變量的復制:復制的是存儲在棧中的指針,將指針復制到棧中未新變量分配的空間中,而這個指針副本和原指針執行存儲在堆中的同一個對象;復制操作結束后,兩個變量實際上將引用同一個對象;因此改變其中的一個,將影響另一個;
三種變量類型檢測
1.???Typeof操作符是檢測基本類型的最佳工具;
2.???如果變量值是null或者對象,typeof?將返回“object”;結合null == null 來判斷
3.???Instanceof用于檢測引用類型,可以檢測到具體的,它是什么類型的實例;
4.???如果變量是給定引用類型的實例,instanceof操作符會返回true;
Object.prototype.toString.call(xx) 來打印原型判斷類型
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
14.你可以用js去實現一個單向、雙向、循環鏈表嗎?你可以實現查找、插入、刪除操作嗎?可以在這里試一下:在線編程環境
鏈表:
插入鏈表節點:
刪除鏈表節點:
雙向鏈表:
循環鏈表:
下面給一個最簡單的單項鏈表示例:
/** ** 先創建一個節點類,記錄當前數據,和下個節點,如果是雙向鏈表,就包含prev ** prev: 對上個節點的引用 ** next: 對下個節點的應用 **/ class Node{ constructor(data){ this.data = data; this.next = null; } } /** ** 創建鏈表,head是鏈表中的一個起始節點,關于單項鏈表,雙向鏈表和循環鏈表參考文章介紹 ** find: 找到數據所在的節點,這里是示例,其實應該有個唯一標識 ** insert: 在指定節點后面插入節點 **/ class LinkTable{ constructor(data){ this.head = null; this.end = null; if(data){ this.head = new Node(data) } } find(data){ let start = this.head; while(start.data != data){ start = start.next; } return start; } insert(data,node){ let nod = new Node(data); nod.next = node.next; item.next = nod; } }
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
14.你了解基本常見算法嗎?快速排序寫一個?要是限制空間利用你該如何寫?快速排序:
(1)在數據集之中,選擇一個元素作為"基準"(pivot)。
(2)所有小于"基準"的元素,都移到"基準"的左邊;所有大于"基準"的元素,都移到"基準"的右邊。
(3)對"基準"左邊和右邊的兩個子集,不斷重復第一步和第二步,直到所有子集只剩下一個元素為止。
選擇排序:
(1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
(2)再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的末尾
(3)直到所有都排序
冒泡排序:
比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元素應該會是最大的數。
針對所有的元素重復以上的步驟,除了最后一個。
持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
直接插入排序:
(1)將待排序數組取一個數值插入到已排序數組中合適的位置
(2)重復取數據,直到所有數據取完
15.你了解貪心算法、動態規劃、分治算法、回溯算法等常見的算法嗎?貪心算法
?所謂貪心算法是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。
?????貪心算法沒有固定的算法框架,算法設計的關鍵是貪心策略的選擇。必須注意的是,貪心算法不是對所有問題都能得到整體最優解,選擇的貪心策略必須具備無后效性,即某個狀態以后的過程不會影響以前的狀態,只與當前狀態有關。
????所以對所采用的貪心策略一定要仔細分析其是否滿足無后效性。
貪心算法的基本思路:
??? 1.建立數學模型來描述問題。
??? 2.把求解的問題分成若干個子問題。
??? 3.對每一子問題求解,得到子問題的局部最優解。
??? 4.把子問題的解局部最優解合成原來解問題的一個解。
動態規劃算法
動態規劃過程是:每次決策依賴于當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。
?? ?基本思想與分治法類似,也是將待求解的問題分解為若干個子問題(階段),按順序求解子階段,前一子問題的解,為后一子問題的求解提供了有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各子問題,最后一個子問題就是初始問題的解。
?? ?由于動態規劃解決的問題多數有重疊子問題這個特點,為減少重復計算,對每一個子問題只解一次,將其不同階段的不同狀態保存在一個二維數組中。
?? ?與分治法最大的差別是:適合于用動態規劃法求解的問題,經分解后得到的子問題往往不是互相獨立的(即下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步的求解)。
能采用動態規劃求解的問題的一般要具有3個性質:
??? (1) 最優化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最優化原理。
??? (2) 無后效性:即某階段狀態一旦確定,就不受這個狀態以后決策的影響。也就是說,某狀態以后的過程不會影響以前的狀態,只與當前狀態有關。
?? (3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被多次使用到。(該性質并不是動態規劃適用的必要條件,但是如果沒有這條性質,動態規劃算法同其他算法相比就不具備優勢)
分治算法
分治法的設計思想是:將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
分治策略是:對于一個規模為n的問題,若該問題可以容易地解決(比如說規模n較小)則直接解決,否則將其分解為k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞歸地解這些子問題,然后將各子問題的解合并得到原問題的解。這種算法設計策略叫做分治法。
分治法所能解決的問題一般具有以下幾個特征:
1) 該問題的規模縮小到一定的程度就可以容易地解決
2) 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。
3) 利用該問題分解出的子問題的解可以合并為該問題的解;
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。
回溯法
在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)
分支限界法
類似于回溯法,也是一種在問題的解空間樹T上搜索問題解的算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出T中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。
由于求解目標不同,導致分支限界法與回溯法在解空間樹T上的搜索方式也不相同。回溯法以深度優先的方式搜索解空間樹T,而分支限界法則以廣度優先或以最小耗費優先的方式搜索解空間樹T。
16.你是如何理解前端架構的?你了解持續集成嗎?架構,我理解主要做:系統分解、服務分層的工作。
持續集成?(Continuous integration,簡稱CI)。項目是一個迭代一個迭代快速開發,每個迭代開發不同的feature,所有的feature合在一起構成完整的功能。
持續集成的目的,就是讓產品可以快速迭代,同時還能保持高質量。它的核心措施是,代碼集成到主干之前,必須通過自動化測試。只要有一個測試用例失敗,就不能集成。
Martin Fowler說過,"持續集成并不能消除Bug,而是讓它們非常容易發現和改正。"
與持續集成相關的,還有兩個概念,分別是持續交付和持續部署。
17.你了解基本的設計模式嗎?舉例單例模式、策略模式、代理模式、迭代模式、發布訂閱模式。。。?設計模式(Design pattern)代表了最佳的實踐,通常被有經驗的面向對象的軟件開發人員所采用。設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。
單例模式:
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
策略模式
在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬于行為型模式。
在策略模式中,我們創建表示各種策略的對象和一個行為隨著策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
很好理解,比如上面給的一個異常處理的代碼,寫個簡單的示例。
export class RuntimeException { constructor(message) { this._message = message; } get name() { return "RuntimeException"; } get message() { return this._message; } toString() { return this.name + ": " + this.message; } } export class IllegalStateException extends RuntimeException { constructor(message) { super(message); } get name() { return "IllegalStateException"; } } export class InvalidArgumentException extends RuntimeException { constructor(message) { super(message); } get name() { return "InvalidArgumentException"; } } export class NotImplementedException extends RuntimeException { constructor(message) { super(message); } get name() { return "NotImplementedException"; } } export function funcWrapper(args){ try{ if(!args) throw new InvalidArgumentException("args undefined") if(args == 1) throw new IllegalStateException("args illegal") }catch(e){ console.log(e.toString()) } }
瀏覽器可以跑下結果看看:
這就是策略模式,不同的情況,輸出的結果是不一樣的。
18.寫一個事件監聽函數唄?實現once、on、remove、emit功能
19.node.js的實現層是什么?
20.node的事件循環機制是怎樣的?node的child_process模塊有幾個api,分別的作用是什么?
22.http1.0與1.1協議的區別?node是如何實現http模塊的?
25.nginx相關配置了解過嗎?
27.小程序架構
28.vue v-model 語法糖?vue push式更新?vue computed和watch的區別?
29.redux dispatch一個action之后的更新過程
connect的時候出于性能優化的考慮做了一層淺比較。
30.假設頁面有多個模塊,每個模塊都用到了getUser方法獲取用戶信息,怎么設計通用的getUser避免發出多次請求。
這個是一個朋友給的網易考拉前端的面試題,朋友給了2個解法:
監聽者模式,監聽getUser事件,第一次注冊監聽事件時發出請求,待請求數據回來,執行所有回調。通過事件監聽解耦。
基于promise,getUser方法提供一個thenable的能力,返回一個promise對象,在Promise中將resolve維護在一個resolveList列表中,其實和事件監聽中的事件列表類似。第一次調用時發出請求,請求回來后調用resolveList列表中所有resolve。
個人感覺基于promise的方法比事件監聽模式更優,本來就是一步的操作返回的也是一個Promise,更符合編碼邏輯。
說到最后,結合朋友的面試分享一下:
朋友說網易考拉前端的面試官姿態比較高,一種專橫跋扈的感覺,沒有耐心,固步自封,整體面試體驗比較差,而且面試官都是比較自以為是那種。朋友說有個面試官明顯沒準備,邊問邊看簡歷,沒有重點和主線,然后讓他提問2個面試官不知道的問題,結果他誰便說了2個,面試官真的不知道,場面很尷尬。而且有幾個面試官在面試中還說錯了一些技術知識點。整體感覺面試官問的問題都是比較分散,很隨意,也反映出網易考拉前端的技術基礎建設也是比較差勁的,技術主管只關注業務實現,對于技術體系建設,技術規劃設計都沒有概念,這種團隊的技術氛圍可想而知。最后朋友說他問了那個技術主管對于技術體系建設和規劃是什么?怎么支撐業務發展?結果也沒說什么。
其實可以考慮阿里、騰訊這種大公司,面試體驗絕對不一樣,技術體系建設,知識面廣度和深度,不是在一個檔次的。能去大廠還是考慮大廠,畢竟大廠是有真正的大牛在。
本文總結了前端老司機經常問題的一些問題并結合個人總結給出了比較詳盡的答案。
關于知識點原理和詳細,參考講堂的視頻教程:前端增長-重新定義大前端
課程知識在不斷更新,本片內容也逐步更新
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/114984.html
摘要:本文總結了前端老司機經常問題的一些問題并結合個人總結給出了比較詳盡的答案。網易阿里騰訊校招社招必備知識點。此外還有網絡線程,定時器任務線程,文件系統處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機制叫做事件循環。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結了前端老司機經常問題的一些問題并結合個...
摘要:本文總結了前端老司機經常問題的一些問題并結合個人總結給出了比較詳盡的答案。網易阿里騰訊校招社招必備知識點。此外還有網絡線程,定時器任務線程,文件系統處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機制叫做事件循環。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結了前端老司機經常問題的一些問題并結合個...
摘要:閉包確實是一個說爛了的概念,校招社招都會被問到,今天總結一番。先下定義,閉包是函數和該函數的詞法作用域的組合。在這個栗子里,函數以及它對變量的引用就構成了閉包。閉包和作用域對于閉包和作用域的關系,我的理解是閉包其實就是作用域的延伸。 閉包確實是一個說爛了的概念,校招社招都會被問到,今天總結一番。先下定義,閉包是函數和該函數的詞法作用域的組合。其實這個定義是比較教條的,可以直白的理解為閉...
摘要:拿到秋招的同學,如確定入職需與用人單位簽署三方協議,以保證雙方的利益不受損失。當然每個崗位所要求的側重點不同,但卻百變不離其宗。方法論要想達成某個目標都有其特定的方法論,學習技術也不例外,掌握適當的學習方法才能事半功倍。 寫在前面的話 筆者從17年的2月份開始準備春招,其中遇到不少坑,也意識到自己走過的彎路。故寫了這篇文章總結一番,本文適合主動學習的,對自己要學的課程不明確的,對面試有...
閱讀 1672·2021-11-15 11:38
閱讀 4514·2021-09-22 15:33
閱讀 2331·2021-08-30 09:46
閱讀 2175·2019-08-30 15:43
閱讀 827·2019-08-30 14:16
閱讀 2068·2019-08-30 13:09
閱讀 1255·2019-08-30 11:25
閱讀 700·2019-08-29 16:42