又到了每一年的面試季,一些換工做的朋友最近也正在加緊複習中,在這裏呢做者整理了十道前端面試中的精選問題和答案,但願對想要換工做的朋友有所幫助,同時若是在閱讀的過程當中發現文章的問題,也請在評論區告知我。javascript
原文連接html
defineProperty
監聽數據變化。指令
來動態渲染模板。考慮如下場景:前端
export default {
data () {
return {
number: 0
};
},
mounted () {
var i = 0;
for(var i = 0; i < 1000; i++) {
this.number++;
}
}
}
複製代碼
當咱們在一個同步任務中執行1000次number++操做,按照set -> 訂閱器(dep) -> 訂閱者(watcher) -> update的過程,dom應該會頻繁更新,按理說這會很消耗性能,但實際上這個過程當中dom只會更新一次。vue
這是由於vue的dom更新是一個異步操做,在數據更新後會首先被set
鉤子監聽到,可是不會立刻執行dom更新,而是在下一輪循環
中執行更新。 具體實現是vue中實現了一個queue
隊列用於存放本次事件循環
中的全部watcher
更新,而且同一個watcher
的更新只會被推入隊列一次,並在本輪事件循環的微任務
執行結束後執行此更新(UI Render
階段),這就是dom只會更新一次的緣由。java
js中的異步任務會進入
任務隊列
在下一輪的事件循環中執行,更多事件循環的內容請參考Javascript運行機制之Event Loop。node
在React 16前,組件的更新是遞歸組件樹的方式實現的,這個遞歸更新的過程是同步的,若是組件樹過於龐大,實際更新過程會形成一些性能上的問題。webpack
在React 16中發佈了React Fiber,React Fiber是一種能將遞歸更新組件樹的任務分片
(time-slicing
)執行的算法。它將組件樹的更新拆分爲多個子任務,在子任務的執行過程當中,容許暫存當前進度而後執行其餘優先級較高的任務
,如在更新過程當中用戶產生了交互,那麼會優先去處理用戶交互,而後在迴歸更新任務繼續執行。更多相關React Fiberweb
從如下角度出發設計面試
考慮到服務器壓力因素,合理使用以上兩種上報方案,(答到服務器壓力點)算法
服務器上的日誌存儲方案有興趣能夠本身瞭解,要是再講一講
GFS
和HDFS
等分佈式文件系統應該有加分。
關於峯值的設定須要考慮到應用使用的高峯段和低谷段,而且此峯值須要在各個場景下不斷調優。
import在編譯時加載的特色使得其效率更高,也讓靜態分析和優化成爲了可能。
webpack的tree shaking優化的基礎就是import的靜態導入。
在node中使用require
和exports
時咱們發現無論是在模塊中仍是在全局對象上,都不存在這兩個方法,那麼這兩個方法是從何而來呢?
其實require方法自己是定義在Module中的,node在編譯階段將js文件包裝在函數將其包裝成模塊:
(function (exports, require, module, __filename, __dirname) {
file_content...
})
複製代碼
使用require加載模塊的過程
path
path
查找是否有緩存var cache = Module._cache[path]
,若是有緩存直接return
http
,若是是直接return
var module = new Module(path, parent);
Module._cache[path] = module;
複製代碼
module.load(path);
攻擊步驟
html
中返回給瀏覽器eg:
//惡意URL: http://xxx.com?key=<script>document.cookie</script>
//服務端拼接
<div>@{{params.key}}</div>
//最終瀏覽器執行了此惡意代碼獲取到用戶cookie
<div>
<script>document.cookie</script>
</div>
複製代碼
攻擊步驟
HTML
中返回給瀏覽器存儲型XSS會將惡意代碼保存在數據庫中,會影響到全部使用的用戶,相比於反射型XSS形成後果更嚴重。
DOM型XSS針對的主要是前端JS代碼的漏洞
攻擊步驟
Javascript
直接執行了這些惡意代碼,形成DOM型XSS攻擊eg:
//惡意URL: http://xxx.com?key=document.cookie
//前端取出key字段並執行
evel(location.key)
複製代碼
防範存儲和反射型XSS
防範DOM型XSS
CSRF攻擊其實是利用了瀏覽器在向A域名發起請求前,會cookie
列表中查詢是否存在A域名的cookie。如果存在,則會將cookie添加至請求頭中的機制。
這個機制不在意請求具體是從哪一個域名發出,它只是關心目標路由。
攻擊步驟
WebA
,瀏覽器保存下WebA
爲此用戶設置的cookie
WebB
,此時從惡意網站WebB
向WebA
發送的請求中已經帶上了用戶的cookie
防範CSRF攻擊
Ajax
跨域請求,在Access-Control-Allow-Origin中設置安全的域名,如WebA
的域名。form
表單請求,後端須要驗證http的Referer字段,確保來源是安全的。羊毛黨
一般使用腳本攻擊咱們的線上活動,得到非法利潤,他們一般使用刷API
接口,自動刷單
等方式獲取利潤。
一般來講,咱們須要人機識別來防範腳本攻擊,在web前端
和服務端
之間,添加一層風控系統,用於鑑別終端是不是機器。
但前端依然能夠爲羊毛黨
增長一些收入難度,想要薅羊毛
先得過前端這一關(紙老虎
)。
token
,由風控系統校驗token
,攻擊者必須破解js生成token
的算法才能進行下一步。斷點調試
tips:在前端的加密過程當中,咱們可使用一些DOM、BOM,API,由於攻擊者經過
API
攻擊沒法直接模擬出真實瀏覽器的環境,就算模擬也須要費一番功夫,加大攻擊者破解算法難度。
HTTP/2經過維護靜態字典和動態字典的方式來壓縮首部
對於靜態字典中匹配的頭部名稱或頭部名稱和值的組合,可使用一個字符表示,如創建鏈接時:
method:GET 可使用 1表示 (徹底匹配) cookeie: xxx 可使用 2:xxx表示 (頭部匹配)
同時將cookeie: xxx加入動態字典中,這樣後續的整個cookie鍵值對均可以使用一個字符表示:
cookeie: xxx 可使用 3表示 (加入到動態字典)
對於靜態字典和動態字典中都不存在的內容,使用了哈夫曼(霍夫曼)編碼來壓縮體積。
更多相關內容請查看詳情HTTP/2新特性
在Js代碼執行時,會產生一個調用棧,執行某個函數時會將其壓入棧,當它 return 後就會出棧。
而從某個函數調用另一個函數時,就會爲被調用的函數創建一個新的棧幀,而且進入這個棧幀,這個棧幀稱爲當前棧,而調用函數的棧幀稱爲調用棧。
function A(){
return 1;
}
function B(){
A();
}
function C(){
B();
}
C();
複製代碼
Js執行棧中除了當前執行函數的棧幀,還保存着調用其函數的棧幀,在A釋放前,執行棧中保存了A、B、C的棧幀,過長的調用棧幀在Js中會致使一個
棧溢出
的錯誤。
棧溢出
的錯誤經常出如今遞歸中。
當遞歸層次較深影響到代碼運行效率,甚至出錯後咱們應該如何優化呢?
function fibonacci (n) {
if ( n <= 1 ) {
return 1
};
return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci(100) //卡死
複製代碼
仔細觀察上述調用過程C -> B -> A,行程此調用棧的主要緣由是在A執行完成後會將執行權返回B,此時B才能釋放,B釋放完成後繼續講執行權返回C,最後C釋放。
尾調用
尾調用(Tail Call)是函數式編程的一個重要概念,是指某個函數的最後一步是調用另外一個函數。 尾調用因爲是函數的最後一步操做,因此不須要保留外層函數的調用幀,因此在C調用B後就會釋放。
尾遞歸
函數調用自身,稱爲遞歸。若是尾調用自身,就稱爲尾遞歸。
將fibonacci函數使用尾遞歸優化
// 尾遞歸的優化每每是經過修改函數參數完成的
function fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return fibonacci2 (n - 1, ac2, ac1 + ac2);
}
複製代碼
面試官:尾調用是ES6的新功能吧,並且只有嚴格模式才能生效,由於在非嚴格模式下,能夠經過
function.caller
追蹤到調用棧,還有其餘方法嗎?
使用蹦牀函數將遞歸轉爲循環執行
function trampoline(fn) {
while (fn && fn instanceof Function) {
fn = fn();
}
return fn;
}
function fibonacci3 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return fibonacci3.bind(null, n - 1, ac2, ac1 + ac2);
}
trampoline(fibonacci3(100)) //573147844013817200000
複製代碼
面試官:嗯,這樣確實能夠避免棧溢出的錯誤問題,那你能嘗試下不使用遞歸思想實現求斐波那契數列的和呢?
function dp(n) {
if(n <= 1){
return 1
}
var a = 1;
var b = 2;
var temp = 0;
for(let i = 2; i < n; i++){
temp = a + b;
a = b;
b = temp;
}
return temp
}
複製代碼
最終咱們對遞歸的優化就是放棄了使用遞歸😃