本文總結了前端老司機常常問題的一些問題並結合我的總結給出了比較詳盡的答案。網易阿里騰訊校招社招必備知識點。
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-你不知道的那些細節
課程知識在不斷更新,本片內容也逐步更新
官方博客:FED123前端學堂javascript
瀏覽器是按照文檔流解析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時間節點。
能夠經過這個接口統計前端的頁面性能數據。css
domainLookupStart - fetchStart = appCache時間,這段時間瀏覽器首先檢查緩存
domainLookupEnd -domainLookupStart = DNS時間
connectEnd - connectStart = TCP時間
responseStart - requestStart = FTTB首字節時間,或者說是服務器響應等待時間
domContentLoad - navigationStart = 頁面pageLoad時間
loadEventEnd - navigationStart = 頁面onLoad時間html
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端前端
參考下:瀏覽器工做原理 瀏覽器渲染與阻塞原理vue
第一部分經過performance.time這個api咱們能夠了解瀏覽器加載網頁的流程,瀏覽器邊加載html邊構建DOM樹,固然會有容錯和修正機制。瀏覽器解析到行內css和內聯css會立馬加入到構建渲染樹,解析到外鏈css就開始加載,加載完以後也會合併到渲染樹的構建中,最後將渲染樹和DOM作節點鏈路匹配,也叫layout階段,計算每一個DOM元素最終在屏幕上顯示的大小和位置。 遍歷順序爲從左至右,從上到下,繪製在屏幕上,layout過程當中可能會觸發頁面迴流和重繪,好比某個外鏈css加載完解析以後合併構建到渲染樹中,其中某個css改變了DOM樹種某個元素的定位(改爲絕對定位)或者改變了長寬邊距等位置信息,會觸發從新layout,因此會迴流reflow。重繪是好比css改變了以前的背景圖片顏色,瀏覽器會從新繪製。java
會有渲染阻塞,瀏覽器刷新的頻率大概是60次/秒, 也就是說刷新一次大概時間爲16ms,若是瀏覽器對每一幀的渲染工做超過了這個時間, 頁面的渲染就會出現卡頓的現象。瀏覽器內核中有3個最主要的線程:JS線程,UI渲染線程,事件處理線程。此外還有http網絡線程,定時器任務線程,文件系統處理線程等等。node
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)react
總結一下,渲染阻塞有兩個方面:webpack
js主線程執行時間長會致使渲染線程阻塞,影響渲染。咱們也稱爲longtask
渲染線程自身阻塞,渲染時間達不到幀率60,會看起來卡頓,好比迴流或者重繪等,或者css效率過低,動畫處理不合適,致使渲染耗時
3.請簡述下js引擎的工做原理,js是怎樣處理事件的eventloop,宏任務源tasks和微任務源jobs分別有哪些?js是如何構造抽象語法樹(AST)的?nginx
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編譯成字節碼後面也是在虛擬機執行),而後就開始執行腳本。
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
擴展性主要是從功能上考慮,容錯性是從數據上考慮。
設計開發組件的時候首先要設計好數據模型,固然能夠和後端共同約定一個標準,後面只要是這部分都用這個標準字段。後面能夠對標準字段作擴展,開發時候要作容錯和數據響應式開發。
功能這部分其實能夠從基礎功能和擴展功能來看,基礎功能能夠在原有組件上作根據數據來展現。擴展功能能夠經過組件結合的形式來處理。
我主要考慮的是組件複用,能夠將一類組件歸類,好比商品卡片,基本都是頭圖加標題行動點,價格,按鈕。這就是最基礎的一個組件。擴展性能夠經過數據來作響應式的展現,好比新增一個描述,數據模型新增描述字段,有描述字段卡片上就展現描述,沒有就不展現。像點擊按鈕的加購功能能夠單獨作成功能組件,統一處理,而不放在卡片上。由於這種加購每每附帶的是商業邏輯,有不少業務邏輯要處理,獨立出來反而更利於維護和拓展。
錯誤處理咱們這邊是基於組件的方式來處理,開發一個錯誤處理的功能組件,提供thenable的能力,區分不一樣的錯誤類型,提供統一埋點作監控和記錄。
參考下: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字段,即便沒有變化。
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
我目前開發分狀況用不一樣的技術框架。
若是單純開發導購頁面,好比一個商品列表頁面,這種爲了加載性能和操做體驗,我是不考慮用框架的,也不用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'; } }
對於前端領域來講,目前前端框架作掉了不少事情,搭建好項目框架以後,開發的就行就是填功能。所編寫的模塊和組件的模式也比較固定,能夠根據具體狀況來實現。
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
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等等,用的時候很方便,這正是聲明式編程最直觀的好處。
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
怎麼可能,我又不是你。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方式定位的祖先類元素(注意,必定要是直系祖先纔算哦~),直到<html>標籤爲止,這裏還須要注意的是,relative和static方式在最外層時是以<body>標籤爲定位原點的,而absoulte方式在無父級是position非static定位時是以<html>做爲原點定位。
參考: position屬性
關於Layout and the containing block,看下官方介紹的contain block,另外相關的點是 BFC:如何建立塊級格式化上下文(block formatting context),BFC有什麼用
參考:瀏覽器渲染流水線解析與網頁動畫性能優化
動畫能夠看作是一個連續的幀序列的組合。咱們把網頁的動畫分紅兩大類 —— 一類是合成器動畫,一類是非合成器動畫(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的性能問題與動畫調試
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
參考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;
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
能夠在這裏試一下:在線編程環境
鏈表:
插入鏈表節點:
刪除鏈表節點:
雙向鏈表:
循環鏈表:
下面給一個最簡單的單項鍊表示例:
/** ** 先建立一個節點類,記錄當前數據,和下個節點,若是是雙向鏈表,就包含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; } }
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端
快速排序:
(1)在數據集之中,選擇一個元素做爲"基準"(pivot)。
(2)全部小於"基準"的元素,都移到"基準"的左邊;全部大於"基準"的元素,都移到"基準"的右邊。
(3)對"基準"左邊和右邊的兩個子集,不斷重複第一步和第二步,直到全部子集只剩下一個元素爲止。
選擇排序:
(1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
(2)再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾
(3)直到全部都排序
冒泡排序:
比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。
對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
針對全部的元素重複以上的步驟,除了最後一個。
持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較。
直接插入排序:
(1)將待排序數組取一個數值插入到已排序數組中合適的位置
(2)重複取數據,直到全部數據取完
所謂貪心算法是指,在對問題求解時,老是作出在當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出的僅是在某種意義上的局部最優解。
貪心算法沒有固定的算法框架,算法設計的關鍵是貪心策略的選擇。必須注意的是,貪心算法不是對全部問題都能獲得總體最優解,選擇的貪心策略必須具有無後效性,即某個狀態之後的過程不會影響之前的狀態,只與當前狀態有關。
因此對所採用的貪心策略必定要仔細分析其是否知足無後效性。
貪心算法的基本思路:
1.創建數學模型來描述問題。
2.把求解的問題分紅若干個子問題。
3.對每一子問題求解,獲得子問題的局部最優解。
4.把子問題的解局部最優解合成原來解問題的一個解。
動態規劃過程是:每次決策依賴於當前狀態,又隨即引發狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,因此,這種多階段最優化決策解決問題的過程就稱爲動態規劃。
基本思想與分治法相似,也是將待求解的問題分解爲若干個子問題(階段),按順序求解子階段,前一子問題的解,爲後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各類可能的局部解,經過決策保留那些有可能達到最優的局部解,丟棄其餘局部解。依次解決各子問題,最後一個子問題就是初始問題的解。
因爲動態規劃解決的問題多數有重疊子問題這個特色,爲減小重複計算,對每個子問題只解一次,將其不一樣階段的不一樣狀態保存在一個二維數組中。
與分治法最大的差異是:適合於用動態規劃法求解的問題,經分解後獲得的子問題每每不是互相獨立的(即下一個子階段的求解是創建在上一個子階段的解的基礎上,進行進一步的求解)。
能採用動態規劃求解的問題的通常要具備3個性質:
(1) 最優化原理:若是問題的最優解所包含的子問題的解也是最優的,就稱該問題具備最優子結構,即知足最優化原理。
(2) 無後效性:即某階段狀態一旦肯定,就不受這個狀態之後決策的影響。也就是說,某狀態之後的過程不會影響之前的狀態,只與當前狀態有關。
(3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被屢次使用到。(該性質並非動態規劃適用的必要條件,可是若是沒有這條性質,動態規劃算法同其餘算法相比就不具有優點)
分治法的設計思想是:將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
分治策略是:對於一個規模爲n的問題,若該問題能夠容易地解決(好比說規模n較小)則直接解決,不然將其分解爲k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞歸地解這些子問題,而後將各子問題的解合併獲得原問題的解。這種算法設計策略叫作分治法。
分治法所能解決的問題通常具備如下幾個特徵:
1) 該問題的規模縮小到必定的程度就能夠容易地解決
2) 該問題能夠分解爲若干個規模較小的相同問題,即該問題具備最優子結構性質。
3) 利用該問題分解出的子問題的解能夠合併爲該問題的解;
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。
在包含問題的全部解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,若是包含,就從該結點出發繼續探索下去,若是該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)
相似於回溯法,也是一種在問題的解空間樹T上搜索問題解的算法。但在通常狀況下,分支限界法與回溯法的求解目標不一樣。回溯法的求解目標是找出T中知足約束條件的全部解,而分支限界法的求解目標則是找出知足約束條件的一個解,或是在知足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。
因爲求解目標不一樣,致使分支限界法與回溯法在解空間樹T上的搜索方式也不相同。回溯法以深度優先的方式搜索解空間樹T,而分支限界法則以廣度優先或以最小耗費優先的方式搜索解空間樹T。
架構,我理解主要作:系統分解、服務分層的工做。
持續集成 (Continuous integration,簡稱CI)。項目是一個迭代一個迭代快速開發,每一個迭代開發不一樣的feature,全部的feature合在一塊兒構成完整的功能。
持續集成的目的,就是讓產品能夠快速迭代,同時還能保持高質量。它的核心措施是,代碼集成到主幹以前,必須經過自動化測試。只要有一個測試用例失敗,就不能集成。
Martin Fowler說過,"持續集成並不能消除Bug,而是讓它們很是容易發現和改正。"
與持續集成相關的,還有兩個概念,分別是持續交付和持續部署。
設計模式(Design pattern)表明了最佳的實踐,一般被有經驗的面向對象的軟件開發人員所採用。設計模式是軟件開發人員在軟件開發過程當中面臨的通常問題的解決方案。
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。
注意:
一、單例類只能有一個實例。
二、單例類必須本身建立本身的惟一實例。
三、單例類必須給全部其餘對象提供這一實例。
在策略模式(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個解法:
我的感受基於promise的方法比事件監聽模式更優,原本就是一步的操做返回的也是一個Promise,更符合編碼邏輯。
說到最後,結合朋友的面試分享一下:
朋友說網易考拉前端的面試官姿態比較高,一種專橫跋扈的感受,沒有耐心,固步自封,總體面試體驗比較差,並且面試官都是比較自覺得是那種。朋友說有個面試官明顯沒準備,邊問邊看簡歷,沒有重點和主線,而後讓他提問2個面試官不知道的問題,結果他誰便說了2個,面試官真的不知道,場面很尷尬。並且有幾個面試官在面試中還說錯了一些技術知識點。總體感受面試官問的問題都是比較分散,很隨意,也反映出網易考拉前端的技術基礎建設也是比較差勁的,技術主管只關注業務實現,對於技術體系建設,技術規劃設計都沒有概念,這種團隊的技術氛圍可想而知。最後朋友說他問了那個技術主管對於技術體系建設和規劃是什麼?怎麼支撐業務發展?結果也沒說什麼。
其實能夠考慮阿里、騰訊這種大公司,面試體驗絕對不同,技術體系建設,知識面廣度和深度,不是在一個檔次的。能去大廠仍是考慮大廠,畢竟大廠是有真正的大牛在。
本文總結了前端老司機常常問題的一些問題並結合我的總結給出了比較詳盡的答案。
關於知識點原理和詳細,參考講堂的視頻教程:前端增加-從新定義大前端課程知識在不斷更新,本片內容也逐步更新