【秋招求職之路】字節(jié)跳動一面復(fù)盤總結(jié)
往期精彩文章推薦
字節(jié)跳動廣告系統(tǒng) 面經(jīng)
一面
自我介紹
手撕防抖(如果滾動條判斷一個div是否存在會用什么來做?節(jié)流)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>實現(xiàn)防抖</title>
<style>
.container {
width: 200px;
height: 200px;
background-color: aqua;
font-size: 30px;
line-height: 200px;
text-align: center;
}
</style>
</head>
<body>
<div class="container"></div>
<script>
var btn = document.getElementsByClassName('container')[0]
// let cnt = 0
// let timeId = null
// btn.onmouseover = () => {
// clearTimeout(timeId)
// timeId = setTimeout(() => {
// btn.innerHTML = ++cnt
// }, 2000)
// }
let cnt = 0
let doSomething = () => {
btn.innerHTML = ++cnt
}
let debounce = (fn,time,triggerNow) => {
let timeId = null
let debounced = (...args) => {
if(timeId){
clearTimeout(timeId)
}
if(triggerNow){
let exec = !timeId
timeId =setTimeout(()=>{
timeId = null
},time)
if(exec){
fn.apply(this,args)
}
}else{
timeId = setTimeout(()=>{
fn.apply(this,args)
},time)
}
}
debounced.remove = () => {
clearTimeout(timeId)
timeId = null
}
return debounced
}
btn.onmouseover = debounce(doSomething,2000,false)
</script>
</body>
</html>
CSS實現(xiàn)三角形
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>畫三角形</title>
<style>
.triggle{
width: 0px;
height: 0px;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-bottom: 40px solid red;
border-right: 40px solid transparent;
margin: 40px;
}
</style>
</head>
<body>
<div class="triggle"></div>
</body>
</html>
了解偽元素和偽類嗎?
參考:CSS偽類和偽元素的區(qū)別 - Web前端工程師面試題講解
盒模型
div:
width=100px;
border: 10px;
padding: 15px;
margin: 10px;
height=100px;
content-box => 100px
border-box => 150px
Vue雙向綁定實現(xiàn) Object.defineProperty() 它有哪些不足點?
只能監(jiān)聽某個屬性,不能對全對象進行監(jiān)聽 需要 for in遍歷找對象中的屬性 不能監(jiān)聽數(shù)組,需要單獨的對數(shù)組進行特異性操作 會污染原對象
data : {
name; 'abc';
age: 23,
},
this.name = 'brown';
this.gender = '男';
如何讓 gender 改變也會讓視圖變化(面試官意思是如何用Vue動態(tài)新增對象屬性,觸發(fā)dom渲染)
背景:項目中因為一些需求需要在JSON中新增一個屬性,也能console出來,但是就是不能在頁面渲染,即不能觸發(fā)視圖更新
其實在vue 中新增屬性應(yīng)該用 $set 這個方法的
1. 添加單個屬性
用 $set()方法,既可以新增屬性,又可以觸發(fā)視圖更新。
this.$set(this.data,”key”,value)
2.添加多個屬性
使用 Object.assign()用原對象與要混合進去的對象的屬性一起創(chuàng)建一個新的對象。
this.obj = Object.assign({}, this.obj, {
age: 18,
name: 'Chocolate',
})
詢問輸出結(jié)果
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//請寫出以下輸出結(jié)果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
參考:一道常被人輕視的前端JS面試題
第一問
先看此題的上半部分做了什么,首先定義了一個叫「Foo的函數(shù)」,之后為Foo創(chuàng)建了一個叫getName的「靜態(tài)屬性」存儲了一個匿名函數(shù),之后為Foo的「原型對象」新創(chuàng)建了一個叫getName的匿名函數(shù)。之后又通過「函數(shù)變量表達(dá)式」創(chuàng)建了一個getName的函數(shù),最后再聲明一個叫getName函數(shù)。
第一問的Foo.getName 自然是訪問Foo函數(shù)上存儲的「靜態(tài)屬性」,自然是2,沒什么可說的。
第二問
第二問,直接調(diào)用 getName 函數(shù)。既然是直接調(diào)用那么就是訪問「當(dāng)前上文作用域內(nèi)」的叫getName的函數(shù),所以跟1 2 3都沒什么關(guān)系。此題有無數(shù)面試者回答為5。此處有兩個坑,「一是變量聲明提升,二是函數(shù)表達(dá)式。」
「變量聲明提升」
即所有聲明變量或聲明函數(shù)都會被提升到當(dāng)前函數(shù)的頂部。例如下代碼:
console.log('x' in window);//true
var x;
x = 0;
代碼執(zhí)行時js引擎會將聲明語句提升至代碼最上方,變?yōu)椋?/p>
var x;
console.log('x' in window);//true
x = 0;
「函數(shù)表達(dá)式」
var getName與 function getName 都是聲明語句,區(qū)別在于var getName 是「函數(shù)表達(dá)式」,而function getName是「函數(shù)聲明」。關(guān)于JS中的各種函數(shù)創(chuàng)建方式可以看 大部分人都會做錯的經(jīng)典JS閉包面試題 這篇文章有詳細(xì)說明。
函數(shù)表達(dá)式最大的問題,在于js會將此代碼拆分為兩行代碼分別執(zhí)行。例如下代碼:
console.log(x);//輸出:function x(){}
var x=1;
function x(){}
實際執(zhí)行的代碼為,先將 var x=1拆分為var x; 和 x = 1;兩行,再將 var x; 和function x(){}兩行提升至最上方變成:
var x;
function x(){}
console.log(x);
x=1;
所以最終函數(shù)聲明的x覆蓋了變量聲明的x,log輸出為x函數(shù)。同理,原題中代碼最終執(zhí)行時的是:
function Foo() {
getName = function () { alert (1); };
return this;
}
var getName;//只提升變量聲明
function getName() { alert (5);}//提升函數(shù)聲明,覆蓋var的聲明
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
getName = function () { alert (4);};//最終的賦值再次覆蓋function getName聲明
getName();//最終輸出4
第三問
第三問的Foo().getName(); 先執(zhí)行了Foo函數(shù),然后調(diào)用Foo函數(shù)的返回值對象的getName屬性函數(shù)。
Foo函數(shù)的第一句 getName = function () { alert (1); }; 是一句函數(shù)賦值語句,注意它沒有var聲明,所以先向當(dāng)前Foo函數(shù)作用域內(nèi)尋找getName變量,沒有。再向當(dāng)前函數(shù)作用域上層,即外層作用域內(nèi)尋找是否含有getName變量,找到了,也就是第二問中的alert(4)函數(shù),將此變量的值賦值為 function(){alert(1)}。
「此處實際上是將外層作用域內(nèi)的getName函數(shù)修改了。」
注意:此處若依然沒有找到會一直向上查找到window對象,若window對象中也沒有g(shù)etName屬性,就在window對象中創(chuàng)建一個getName變量。
簡單的講,「this的指向是由所在函數(shù)的調(diào)用方式?jīng)Q定的」。而此處的直接調(diào)用方式,this指向window對象。
遂Foo函數(shù)返回的是window對象,相當(dāng)于執(zhí)行 window.getName() ,而window中的getName已經(jīng)被修改為alert(1),所以最終會輸出1
此處考察了兩個知識點,一個是「變量作用域問題」,一個是「this指向問題」。
第四問
直接調(diào)用getName函數(shù),相當(dāng)于 window.getName() ,因為這個變量已經(jīng)被Foo函數(shù)執(zhí)行時修改了,遂結(jié)果與第三問相同,為1
第五問
new Foo.getName(); ,此處考察的是js的運算符優(yōu)先級問題。
js運算符優(yōu)先級:
通過查上表可以得知點(.)的優(yōu)先級高于new操作,遂相當(dāng)于是:
new (Foo.getName)();
所以實際上將getName函數(shù)作為了構(gòu)造函數(shù)來執(zhí)行,遂彈出2。
第六問
第六問 new Foo().getName(),首先看運算符優(yōu)先級「括號高于new」,并且?guī)?shù)的new操作符是優(yōu)先級最高的,實際執(zhí)行為
(new Foo()).getName()
遂先執(zhí)行Foo函數(shù),而Foo此時作為構(gòu)造函數(shù)卻有返回值,所以這里需要說明下js中的「構(gòu)造函數(shù)返回值問題」。
原題中,返回的是this,而this在構(gòu)造函數(shù)中本來就代表當(dāng)前實例化對象,遂最終Foo函數(shù)「返回實例化對象」。之后調(diào)用實例化對象的getName函數(shù),因為在「Foo構(gòu)造函數(shù)中沒有為實例化對象添加任何屬性」,遂到當(dāng)前對象的原型對象(prototype)中尋找getName,找到了。
遂最終輸出3。
第七問
最終實際執(zhí)行為:
new ((new Foo()).getName)();
先初始化Foo的實例化對象,然后將其原型上的getName函數(shù)作為構(gòu)造函數(shù)再次new。
遂最終結(jié)果為3
答案
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//答案:
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3
promise相關(guān),下面代碼輸出結(jié)果
Promise.reject(2).catch(e => e).then(d => {
console.log(d);
});
// 輸出2
一般總是建議,Promise 對象后面要跟catch方法,這樣可以處理 Promise 內(nèi)部發(fā)生的錯誤。「catch方法返回的還是一個 Promise 對象,因此后面還可以接著調(diào)用then方法。」
參考:你真的完全掌握了promise么?
Promise 必知必會(十道題)
關(guān)于 Promise 的 9 個面試題
cookie 和 session 區(qū)別(session 存放哪)
http是一個無狀態(tài)協(xié)議
什么是無狀態(tài)呢?就是說這一次請求和上一次請求是沒有任何關(guān)系的,互不認(rèn)識的,沒有關(guān)聯(lián)的。這種無狀態(tài)的的好處是快速。壞處是假如我們想要把www.zhihu.com/login.html 和 www.zhihu.com/index.html關(guān)聯(lián)起來,必須使用某些手段和工具
cookie和session
由于http的無狀態(tài)性,為了使某個域名下的所有網(wǎng)頁能夠共享某些數(shù)據(jù),session和cookie出現(xiàn)了。客戶端訪問服務(wù)器的流程如下:
首先,客戶端會發(fā)送一個http請求到服務(wù)器端。 服務(wù)器端接受客戶端請求后,建立一個session,并發(fā)送一個http響應(yīng)到客戶端,這個響應(yīng)頭,其中就包含Set-Cookie頭部。該頭部包含了sessionId。Set-Cookie格式如下: Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]在客戶端發(fā)起的第二次請求,假如服務(wù)器給了set-Cookie,瀏覽器會自動在請求頭中添加cookie 服務(wù)器接收請求,分解cookie,驗證信息,核對成功后返回response給客戶端
「注意」
cookie只是實現(xiàn)session的其中一種方案。雖然是最常用的,但并不是唯一的方法。禁用cookie后還有其他方法存儲,比如放在url中 「現(xiàn)在大多都是Session + Cookie」,但是只用session不用cookie,或是只用cookie,不用session在理論上都可以保持會話狀態(tài)。可是實際中因為多種原因,一般不會單獨使用 用session只需要在客戶端保存一個id,實際上「大量數(shù)據(jù)都是保存在服務(wù)端」。如果全部用cookie,數(shù)據(jù)量大的時候客戶端是沒有那么多空間的。 如果只用cookie不用session,那么賬戶信息全部保存在客戶端,一旦被劫持,全部信息都會泄露。并且客戶端數(shù)據(jù)量變大,網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)量也會變大
拓展:token
token 也稱作令牌,由uid+time+sign[+固定參數(shù)]
token 的認(rèn)證方式類似于「臨時的證書簽名」, 并且是一種服務(wù)端無狀態(tài)的認(rèn)證方式, 非常適合于 REST API(表現(xiàn)層狀態(tài)轉(zhuǎn)換) 的場景. 所謂無狀態(tài)就是服務(wù)端并不會保存身份認(rèn)證相關(guān)的數(shù)據(jù)。
「組成」
uid: 用戶唯一身份標(biāo)識 time: 當(dāng)前時間的時間戳 sign: 簽名, 使用 hash/encrypt壓縮成定長的十六進制字符串,以防止第三方惡意拼接 固定參數(shù)(可選): 將一些常用的固定參數(shù)加入到 token 中是為了避免重復(fù)查庫
「存放」
token在客戶端一般存放于localStorage,cookie,或sessionStorage中。在服務(wù)器一般存于數(shù)據(jù)庫中
token認(rèn)證流程
token 的認(rèn)證流程與cookie很相似
用戶登錄,成功后服務(wù)器返回Token給客戶端。 客戶端收到數(shù)據(jù)后保存在客戶端 客戶端再次訪問服務(wù)器,將token放入 headers中服務(wù)器端「采用filter過濾器校驗」。校驗成功則返回請求數(shù)據(jù),校驗失敗則返回錯誤碼
token可以抵抗csrf,cookie+session不行
假如用戶正在登陸銀行網(wǎng)頁,同時登陸了攻擊者的網(wǎng)頁,并且銀行網(wǎng)頁未對csrf攻擊進行防護。攻擊者就可以在網(wǎng)頁放一個表單,該表單提交src為http://www.bank.com/api/transfer,body為count=1000&to=Tom。倘若是session+cookie,用戶打開網(wǎng)頁的時候就已經(jīng)轉(zhuǎn)給Tom1000元了.因為「form 發(fā)起的 POST 請求并不受到瀏覽器同源策略的限制」,因此可以任意地使用其他域的 Cookie 向其他域發(fā)送 POST 請求,形成 CSRF 攻擊。在post請求的瞬間,cookie會被瀏覽器自動添加到請求頭中。但token不同,token是開發(fā)者「為了防范csrf而特別設(shè)計的令牌」,瀏覽器不會自動添加到headers里,攻擊者也無法訪問用戶的token,所以提交的表單「無法通過服務(wù)器過濾」,也就無法形成攻擊。
分布式情況下的session和token
負(fù)載均衡多服務(wù)器的情況,不好確認(rèn)當(dāng)前用戶是否登錄,因為多服務(wù)器不共享session。該解決方案是 session 數(shù)據(jù)持久化,「寫入數(shù)據(jù)庫或別的持久層」。各種服務(wù)收到請求后,都向持久層請求數(shù)據(jù)。這種方案的優(yōu)點是架構(gòu)清晰,缺點是工程量比較大。另外,持久層萬一掛了,就會單點失敗。
而token是無狀態(tài)的,token字符串里就「保存了所有的用戶信息」
客戶端登陸傳遞信息給服務(wù)端,服務(wù)端收到后把用戶信息加密(token)傳給客戶端,客戶端將token存放于localStroage等容器中。客戶端每次訪問都傳遞token,服務(wù)端解密token,就知道這個用戶是誰了。通過cpu加解密,服務(wù)端就不需要存儲session占用存儲空間,就很好的解決負(fù)載均衡多服務(wù)器的問題了。這個方法叫做JWT(Json Web Token)
Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)((RFC 7519).該token被設(shè)計為緊湊且安全的,特別適用于
分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。
參考:阮一峰 JSON Web Token 入門教程
總結(jié)
session存儲于服務(wù)器,可以理解為一個狀態(tài)列表,擁有一個唯一識別符號sessionId,通常存放于cookie中。服務(wù)器收到cookie后解析出sessionId,再去session列表中查找,才能找到相應(yīng)session所依賴cookie cookie類似一個令牌,裝有sessionId,存儲在客戶端,瀏覽器通常會自動添加。 token也類似一個令牌,無狀態(tài),用戶信息都被加密到token中,服務(wù)器收到token后解密就可知道是哪個用戶。需要開發(fā)者手動添加。 jwt只是一個跨域認(rèn)證的方案
如何保護cookie
Secure和HttpOnly
Secure屬性意味著把cookie通信限制在加密傳輸中,指示瀏覽器只能通過安全/加密連接使用cookie。然而如果一個web服務(wù)器在非安全連接中給cookie設(shè)置了一個secure屬性,這個cookie在發(fā)送給用戶時仍然可以通過「中間人攻擊」攔截到。因此,為了安全「必須通過安全連接」設(shè)置cookie的Secure屬性。
HttpOnly屬性指示瀏覽器除了HTTP/HTTPS請求之外不要顯示cookie。這意味著這種cookie不能在客戶端通過腳本獲取,因此也不會輕易的被跨站腳本竊取。
瀏覽器設(shè)置
大部分瀏覽器都支持cookie,并且允許用戶禁止掉他們。下面是一些常用的選項:
完全允許或者禁止cookie,以便瀏覽器總是接受或者總是阻止cookie 通過cookie管理器查看或者刪除cookie 徹底清除所有的隱私數(shù)據(jù),包括cookie
HTTPS中 TLS握手過程簡述
參考:三元博客 (傳統(tǒng)RSA版本)HTTPS為什么讓數(shù)據(jù)傳輸更安全?
參考:TLS1.2 握手的過程是怎樣的?
Koa 中間件 passport
仿美團項目登錄怎么實現(xiàn)的
瀏覽器緩存 強緩存 和 協(xié)商緩存 狀態(tài)碼
結(jié)果
一面通過
小獅子有話說
我是小獅子團隊的【一百個Chocolate】,全網(wǎng)同名,周更的前端博主,分享一些前端技術(shù)干貨與程序員生活日常,歡迎各位小伙伴的持續(xù)關(guān)注,一起變優(yōu)秀~
學(xué)如逆水行舟,不進則退
點擊【在看】可能會有紅包福利出現(xiàn)~
