前端面试-精简版
一、CSS相關
#1.1 左邊定寬,右邊自適應方案:float + margin,float + calc
/* 方案1 */ .left {width: 120px;float: left; } .right {margin-left: 120px; } /* 方案2 */ .left {width: 120px;float: left; } .right {width: calc(100% - 120px);float: left; }#1.2 左右兩邊定寬,中間自適應:float,float + calc, 圣杯布局(設置BFC,margin負值法),flex
.wrap {width: 100%;height: 200px; } .wrap > div {height: 100%; } /* 方案1 */ .left {width: 120px;float: left; } .right {float: right;width: 120px; } .center {margin: 0 120px; } /* 方案2 */ .left {width: 120px;float: left; } .right {float: right;width: 120px; } .center {width: calc(100% - 240px);margin-left: 120px; } /* 方案3 */ .wrap {display: flex; } .left {width: 120px; } .right {width: 120px; } .center {flex: 1; }#1.3 左右居中
- 行內元素:?text-align: center
- 定寬塊狀元素: 左右?margin?值為?auto
- 不定寬塊狀元素:?table布局,position + transform
#1.4 上下垂直居中
- 定高:margin,position + margin(負值)
- 不定高:position?+?transform,flex,IFC + vertical-align:middle
#1.5 盒模型:content(元素內容) + padding(內邊距) + border(邊框) + margin(外邊距)
延伸:?box-sizing
- content-box:默認值,總寬度 =?margin?+?border?+?padding?+?width
- border-box:盒子寬度包含?padding?和?border,總寬度 = margin + width
- inherit:從父元素繼承?box-sizing?屬性
#1.6 BFC、IFC、GFC、FFC:FC(Formatting Contexts),格式化上下文
BFC:塊級格式化上下文,容器里面的子元素不會在布局上影響到外面的元素,反之也是如此(按照這個理念來想,只要脫離文檔流,肯定就能產生?BFC)。產生?BFC?方式如下
- float?的值不為?none。
- overflow?的值不為?visible。
- position?的值不為?relative?和?static。
- display?的值為?table-cell,?table-caption,?inline-block中的任何一個
用處?常見的多欄布局,結合塊級別元素浮動,里面的元素則是在一個相對隔離的環境里運行
IFC:內聯格式化上下文,IFC?的?line?box(線框)高度由其包含行內元素中最高的實際高度計算而來(不受到豎直方向的?padding/margin?影響)。
IFC中的line box一般左右都貼緊整個?IFC,但是會因為?float?元素而擾亂。float?元素會位于 IFC 與?line box?之間,使得?line box?寬度縮短。 同個?ifc?下的多個?line box?高度會不同。?IFC中時不可能有塊級元素的,當插入塊級元素時(如?p?中插入?div)會產生兩個匿名塊與?div?分隔開,即產生兩個?IFC?,每個?IFC?對外表現為塊級元素,與?div?垂直排列。
用處?
- 水平居中:當一個塊要在環境中水平居中時,設置其為?inline-block?則會在外層產生IFC,通過?text-align?則可以使其水平居中。
- 垂直居中:創建一個?IFC,用其中一個元素撐開父元素的高度,然后設置其?vertical-align:?middle,其他行內元素則可以在此父元素下垂直居中
- GFC:網格布局格式化上下文(display: grid)
- FFC:自適應格式化上下文(display: flex)
#二、JS 基礎(ES5)
#2.1 原型
這里可以談很多,只要圍繞?[[ prototype ]]?談,都沒啥問題
#2.2 閉包
牽扯作用域,可以兩者聯系起來一起談
#2.3 作用域
詞法作用域,動態作用域
#2.4 this
不同情況的調用,this指向分別如何。順帶可以提一下?es6?中箭頭函數沒有?this,?arguments,?super?等,這些只依賴包含箭頭函數最接近的函數
#2.5 call,apply,bind 三者用法和區別
參數、綁定規則(顯示綁定和強綁定),運行效率(最終都會轉換成一個一個的參數去運行)、運行情況(call,apply?立即執行,bind?是return?出一個?this?“固定”的函數,這也是為什么?bind?是強綁定的一個原因)
注:“固定”這個詞的含義,它指的固定是指只要傳進去了?context,則?bind?中?return?出來的函數?this?便一直指向?context,除非?context?是個變量
#2.6 變量聲明提升
js?代碼在運行前都會進行?AST?解析,函數申明默認會提到當前作用域最前面,變量申明也會進行提升。但賦值不會得到提升。關于?AST?解析,這里也可以說是形成詞法作用域的主要原因
#三、JS 基礎(ES6)
#3.1 let,const
let?產生塊級作用域(通常配合?for?循環或者?{}?進行使用產生塊級作用域),const?申明的變量是常量(內存地址不變)
#3.2 Promise
這里你談?promise的時候,除了將他解決的痛點以及常用的?API?之外,最好進行拓展把?eventloop?帶進來好好講一下,microtask(微任務)、macrotask(任務) 的執行順序,如果看過?promise?源碼,最好可以談一談 原生?Promise?是如何實現的。Promise?的關鍵點在于callback?的兩個參數,一個是?resovle,一個是?reject。還有就是?Promise?的鏈式調用(Promise.then(),每一個?then?都是一個責任人)
#3.3 Generator
遍歷器對象生成函數,最大的特點是可以交出函數的執行權
- function?關鍵字與函數名之間有一個星號;
- 函數體內部使用?yield表達式,定義不同的內部狀態;
- next指針移向下一個狀態
這里你可以說說?Generator的異步編程,以及它的語法糖?async?和?awiat,傳統的異步編程。ES6?之前,異步編程大致如下
- 回調函數
- 事件監聽
- 發布/訂閱
傳統異步編程方案之一:協程,多個線程互相協作,完成異步任務。
#3.4 async、await
Generator?函數的語法糖。有更好的語義、更好的適用性、返回值是?Promise。
- async => *
- await => yield
注:最好把2,3,4 連到一起講
#3.5 AMD,CMD,CommonJs,ES6 Module:解決原始無模塊化的痛點
- AMD:requirejs?在推廣過程中對模塊定義的規范化產出,提前執行,推崇依賴前置
- CMD:seajs?在推廣過程中對模塊定義的規范化產出,延遲執行,推崇依賴就近
- CommonJs:模塊輸出的是一個值的?copy,運行時加載,加載的是一個對象(module.exports?屬性),該對象只有在腳本運行完才會生成
- ES6 Module:模塊輸出的是一個值的引用,編譯時輸出接口,ES6模塊不是對象,它對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
#四、框架相關
#4.1 數據雙向綁定原理:常見數據綁定的方案
- Object.defineProperty(vue):劫持數據的?getter?和?setter
- 臟值檢測(angularjs):通過特定事件進行輪循 發布/訂閱模式:通過消息發布并將消息進行訂閱
#4.2 VDOM:三個 part
- 虛擬節點類,將真實?DOM節點用?js?對象的形式進行展示,并提供?render?方法,將虛擬節點渲染成真實?DOM
- 節點?diff?比較:對虛擬節點進行?js?層面的計算,并將不同的操作都記錄到?patch?對象
- re-render:解析?patch?對象,進行?re-render
補充1:VDOM 的必要性?
- 創建真實DOM的代價高:真實的?DOM?節點?node?實現的屬性很多,而?vnode?僅僅實現一些必要的屬性,相比起來,創建一個?vnode?的成本比較低。
- 觸發多次瀏覽器重繪及回流:使用?vnode?,相當于加了一個緩沖,讓一次數據變動所帶來的所有?node?變化,先在?vnode?中進行修改,然后?diff?之后對所有產生差異的節點集中一次對?DOM tree?進行修改,以減少瀏覽器的重繪及回流。
補充2:vue 為什么采用 vdom?
引入?Virtual DOM?在性能方面的考量僅僅是一方面。
- 性能受場景的影響是非常大的,不同的場景可能造成不同實現方案之間成倍的性能差距,所以依賴細粒度綁定及?Virtual DOM?哪個的性能更好還真不是一個容易下定論的問題。
- Vue?之所以引入了?Virtual DOM,更重要的原因是為了解耦?HTML依賴,這帶來兩個非常重要的好處是:
- 不再依賴?HTML?解析器進行模版解析,可以進行更多的?AOT?工作提高運行時效率:通過模版?AOT?編譯,Vue?的運行時體積可以進一步壓縮,運行時效率可以進一步提升;
- 可以渲染到?DOM?以外的平臺,實現?SSR、同構渲染這些高級特性,Weex等框架應用的就是這一特性。
綜上,Virtual DOM?在性能上的收益并不是最主要的,更重要的是它使得?Vue?具備了現代框架應有的高級特性。
#4.3 vue 和 react 區別
- 相同點:都支持?ssr,都有?vdom,組件化開發,實現?webComponents?規范,數據驅動等
- 不同點:vue?是雙向數據流(當然為了實現單數據流方便管理組件狀態,vuex?便出現了),react?是單向數據流。vue的?vdom?是追蹤每個組件的依賴關系,不會渲染整個組件樹,react?每當應該狀態被改變時,全部子組件都會?re-render
#4.4 為什么用 vue
簡潔、輕快、舒服
#五、網絡基礎類
#5.1 跨域
很多種方法,但萬變不離其宗,都是為了搞定同源策略。重用的有?jsonp、iframe、cors、img、HTML5 postMessage等等。其中用到?html?標簽進行跨域的原理就是?html?不受同源策略影響。但只是接受?Get?的請求方式,這個得清楚。
延伸1:img iframe script 來發送跨域請求有什么優缺點?
1.?iframe
- 優點:跨域完畢之后DOM操作和互相之間的JavaScript調用都是沒有問題的
- 缺點:1.若結果要以URL參數傳遞,這就意味著在結果數據量很大的時候需要分割傳遞,巨煩。2.還有一個是iframe本身帶來的,母頁面和iframe本身的交互本身就有安全性限制。
2. script
- 優點:可以直接返回json格式的數據,方便處理
- 缺點:只接受GET請求方式
3. 圖片ping
- 優點:可以訪問任何url,一般用來進行點擊追蹤,做頁面分析常用的方法
- 缺點:不能訪問響應文本,只能監聽是否響應
延伸2:配合 webpack 進行反向代理?
webpack?在?devServer?選項里面提供了一個?proxy?的參數供開發人員進行反向代理
'/api': {target: 'http://www.example.com', // your target hostchangeOrigin: true, // needed for virtual hosted sitespathRewrite: {'^/api': '' // rewrite path} },然后再配合?http-proxy-middleware?插件對?api?請求地址進行代理
const express = require('express'); const proxy = require('http-proxy-middleware'); // proxy api requests const exampleProxy = proxy(options); // 這里的 options 就是 webpack 里面的 proxy 選項對應的每個選項// mount `exampleProxy` in web server const app = express(); app.use('/api', exampleProxy); app.listen(3000);然后再用?nginx?把允許跨域的源地址添加到報頭里面即可
說到?nginx?,可以再談談?CORS?配置,大致如下
location / {if ($request_method = 'OPTIONS') {add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Credentials' 'true';add_header 'Access-Control-Allow-Headers' 'DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type'; add_header 'Access-Control-Max-Age' 86400; add_header 'Content-Type' 'text/plain charset=UTF-8'; add_header 'Content-Length' 0; return 200; } }#5.2 http 無狀態無連接
- http?協議對于事務處理沒有記憶能力
- 對同一個url請求沒有上下文關系
- 每次的請求都是獨立的,它的執行情況和結果與前面的請求和之后的請求是無直接關系的,它不會受前面的請求應答情況直接影響,也不會直接影響后面的請求應答情況
- 服務器中沒有保存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求服務器
- 人生若只如初見,請求過的資源下一次會繼續進行請求
http協議無狀態中的 狀態 到底指的是什么?!
- 【狀態】的含義就是:客戶端和服務器在某次會話中產生的數據
- 那么對應的【無狀態】就意味著:這些數據不會被保留
- 通過增加cookie和session機制,現在的網絡請求其實是有狀態的
- 在沒有狀態的http協議下,服務器也一定會保留你每次網絡請求對數據的修改,但這跟保留每次訪問的數據是不一樣的,保留的只是會話產生的結果,而沒有保留會話
#5.3 http-cache:就是 http 緩存
1. 首先得明確 http 緩存的好處
- 減少了冗余的數據傳輸,減少網費
- 減少服務器端的壓力
- Web?緩存能夠減少延遲與網絡阻塞,進而減少顯示某個資源所用的時間
- 加快客戶端加載網頁的速度
2. 常見 http 緩存的類型
- 私有緩存(一般為本地瀏覽器緩存)
- 代理緩存
3. 然后談談本地緩存
本地緩存是指瀏覽器請求資源時命中了瀏覽器本地的緩存資源,瀏覽器并不會發送真正的請求給服務器了。它的執行過程是
- 第一次瀏覽器發送請求給服務器時,此時瀏覽器還沒有本地緩存副本,服務器返回資源給瀏覽器,響應碼是200 OK,瀏覽器收到資源后,把資源和對應的響應頭一起緩存下來
- 第二次瀏覽器準備發送請求給服務器時候,瀏覽器會先檢查上一次服務端返回的響應頭信息中的Cache-Control,它的值是一個相對值,單位為秒,表示資源在客戶端緩存的最大有效期,過期時間為第一次請求的時間減去Cache-Control的值,過期時間跟當前的請求時間比較,如果本地緩存資源沒過期,那么命中緩存,不再請求服務器
- 如果沒有命中,瀏覽器就會把請求發送給服務器,進入緩存協商階段。
與本地緩存相關的頭有:Cache-Control、Expires,Cache-Control有多個可選值代表不同的意義,而Expires就是一個日期格式的絕對值。
3.1 Cache-Control
Cache-Control是HTPP緩存策略中最重要的頭,它是HTTP/1.1中出現的,它由如下幾個值
- no-cache:不使用本地緩存。需要使用緩存協商,先與服務器確認返回的響應是否被更改,如果之前的響應中存在ETag,那么請求的時候會與服務端驗證,如果資源未被更改,則可以避免重新下載
- no-store:直接禁止游覽器緩存數據,每次用戶請求該資源,都會向服務器發送一個請求,每次都會下載完整的資源
- public:可以被所有的用戶緩存,包括終端用戶和CDN等中間代理服務器。
- private:只能被終端用戶的瀏覽器緩存,不允許CDN等中繼緩存服務器對其緩存。
- max-age:從當前請求開始,允許獲取的響應被重用的最長時間(秒)。
3.2 Expires
Expires是HTTP/1.0出現的頭信息,同樣是用于決定本地緩存策略的頭,它是一個絕對時間,時間格式是如Mon, 10 Jun 2015 21:31:12 GMT,只要發送請求時間是在Expires之前,那么本地緩存始終有效,否則就會去服務器發送請求獲取新的資源。如果同時出現Cache-Control:max-age和Expires,那么max-age優先級更高。他們可以這樣組合使用
Cache-Control: public Expires: Wed, Jan 10 2018 00:27:04 GMT3.3 所謂的緩存協商
當第一次請求時服務器返回的響應頭中存在以下情況時
- 沒有?Cache-Control?和?Expires
- Cache-Control?和?Expires?過期了
- Cache-Control?的屬性設置為?no-cache?時
那么瀏覽器第二次請求時就會與服務器進行協商,詢問瀏覽器中的緩存資源是不是舊版本,需不需要更新,此時,服務器就會做出判斷,如果緩存和服務端資源的最新版本是一致的,那么就無需再次下載該資源,服務端直接返回304 Not Modified?狀態碼,如果服務器發現瀏覽器中的緩存已經是舊版本了,那么服務器就會把最新資源的完整內容返回給瀏覽器,狀態碼就是200 Ok,那么服務端是根據什么來判斷瀏覽器的緩存是不是最新的呢?其實是根據HTTP的另外兩組頭信息,分別是:Last-Modified/If-Modified-Since?與?ETag/If-None-Match。
Last-Modified 與 If-Modified-Since
- 瀏覽器第一次請求資源時,服務器會把資源的最新修改時間Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT放在響應頭中返回給瀏覽器
- 第二次請求時,瀏覽器就會把上一次服務器返回的修改時間放在請求頭If-Modified-Since:Thu, 29 Dec 2011 18:23:55發送給服務器,服務器就會拿這個時間跟服務器上的資源的最新修改時間進行對比
如果兩者相等或者大于服務器上的最新修改時間,那么表示瀏覽器的緩存是有效的,此時緩存會命中,服務器就不再返回內容給瀏覽器了,同時Last-Modified頭也不會返回,因為資源沒被修改,返回了也沒什么意義。如果沒命中緩存則最新修改的資源連同Last-Modified頭一起返回
# 第一次請求返回的響應頭 Cache-Control:max-age=3600 Expires: Fri, Jan 12 2018 00:27:04 GMT Last-Modified: Wed, Jan 10 2018 00:27:04 GMT # 第二次請求的請求頭信息 If-Modified-Since: Wed, Jan 10 2018 00:27:04 GMT這組頭信息是基于資源的修改時間來判斷資源有沒有更新,另一種方式就是根據資源的內容來判斷,就是接下來要討論的?ETag?與?If-None-Match
ETag與If-None-Match
ETag/If-None-Match與Last-Modified/If-Modified-Since的流程其實是類似的,唯一的區別是它基于資源的內容的摘要信息(比如MD5 hash)來判斷
瀏覽器發送第二次請求時,會把第一次的響應頭信息ETag的值放在If-None-Match的請求頭中發送到服務器,與最新的資源的摘要信息對比,如果相等,取瀏覽器緩存,否則內容有更新,最新的資源連同最新的摘要信息返回。用ETag的好處是如果因為某種原因到時資源的修改時間沒改變,那么用ETag就能區分資源是不是有被更新。
# 第一次請求返回的響應頭:Cache-Control: public, max-age=31536000 ETag: "15f0fff99ed5aae4edffdd6496d7131f" # 第二次請求的請求頭信息:If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"#5.4 cookie 和 session
- session: 是一個抽象概念,開發者為了實現中斷和繼續等操作,將?user agent和?server?之間一對一的交互,抽象為“會話”,進而衍生出“會話狀態”,也就是?session?的概念
- cookie:它是一個世紀存在的東西,http?協議中定義在?header?中的字段,可以認為是?session?的一種后端無狀態實現
現在我們常說的?session,是為了繞開?cookie?的各種限制,通常借助?cookie本身和后端存儲實現的,一種更高級的會話狀態實現
session?的常見實現要借助cookie來發送?sessionID
#5.5 安全問題,如 XSS 和 CSRF
- XSS:跨站腳本攻擊,是一種網站應用程序的安全漏洞攻擊,是代碼注入的一種。常見方式是將惡意代碼注入合法代碼里隱藏起來,再誘發惡意代碼,從而進行各種各樣的非法活動
防范:記住一點 “所有用戶輸入都是不可信的”,所以得做輸入過濾和轉義
- CSRF:跨站請求偽造,也稱?XSRF,是一種挾制用戶在當前已登錄的Web應用程序上執行非本意的操作的攻擊方法。與?XSS?相比,XSS利用的是用戶對指定網站的信任,CSRF利用的是網站對用戶網頁瀏覽器的信任。
防范:用戶操作驗證(驗證碼),額外驗證機制(token使用)等
總結
- 上一篇: 前端-计算机基础
- 下一篇: 前端面试-综合问题版