投遞初
春招過去了,面了兩次騰訊,兩次一面直接gg,但這兩份經歷對我是有很大幫助的,體會到了一定差距。但在6月份,看了牛客網討論區,又發現了字節跳動一直在招人,從未停止過,而且正好某天瞧見了內推碼,於是乎,我又想嘗試一下了,趕緊迭代一下簡歷,投!投!投!
要求
1、計算機、軟件相關專業;
2、熟悉HTML, CSS, JavaScript和HTTP協議基本知識;
3、熟悉常用的數據結構以及其使用場景;
4、有參與設計和實現的項目(無論大小);
5、瞭解瀏覽器調試工具;
6、有強烈的求知慾和進取心,具有紮實的編程功底,良好的編程習慣。
加分項:
1、有過數據可視化相關經驗;
2、瞭解JavaScript依賴管理;
3、瞭解Webpack, React, SASS, ES6基本原理以及它們解決的問題;
4、瞭解過不限於Node.js, Python, Ruby的任意一門腳本語言;
5、有Github賬號並有項目。
投遞完
不得不說,字節的處理效率真的是高,2號投的,3號就收到了HR小姐姐的電話詢問,簡單詢問做過什麼項目,用的是什麼技術棧,然後有沒有用過React,最後一句:如果後續簡歷通過了,我們會聯繫您進行面試。
然後再5號,下午還在做網絡安全的實驗,就收到微信消息,HR小姐姐加我然後跟我約下週面試時間。最後,選擇了8號(週一)晚上7點30約一面,話不多說,我們進入正題。
面經
自我介紹
熟悉的開頭,這裏就不多說了,跳過…
地址欄輸入url,然後經歷了什麼,瀏覽器此時又經歷了什麼
從URL輸入到頁面展現到底發生什麼?
推薦閱讀:(建議精讀)HTTP靈魂之問,鞏固你的 HTTP 知識體系
瀏覽器解析渲染頁面
瀏覽器解析渲染頁面分爲一下五個步驟:
- 根據 HTML 解析出 DOM 樹
- 根據 CSS 解析生成 CSS 規則樹
- 結合 DOM 樹和 CSS 規則樹,生成渲染樹
- 根據渲染樹計算每一個節點的信息
- 根據計算好的信息繪製頁面
看了我的簡歷,說使用過Koa2,提出爲什麼要使用Koa2,怎麼不用express呢?
Koa2中間件你瞭解過嘛?
項目中有提到用戶數據&狀態,然後問:你登錄攔截有具體瞭解怎麼實現的麼?
其實,面試官就是看到了這樣一句話:
瀏覽器發送一個 request 請求,根據 cookie ,服務器通過 passport 與 redis來驗證當前是否是登錄狀態,返回 username。
主要是項目忘了,我就提了一句用了passport中的一個函數,isAu…來着的,單詞不會讀
面試官也是覺得可能再問下去沒啥必要了,就說,我們來搞點基礎吧~
主要是三大模塊,CSS,JS,算法
考察CSS
css樣式優先級,以及渲染過程
- 內聯樣式(例如, style="…")
- ID選擇器(例如, #example)
- 類選擇器(例如, .example)、屬性選擇器(例如, [type=“radio”])、僞類(例如, :hover)
- 類型選擇器(例如, h1)、僞元素(例如, ::before)
- 繼承的樣式
下面樣式是怎樣渲染的
.container .inner div{
width: 100px;
height: 100px;
}
我說的是從右到左,這確實是沒有問題的,面試官反問,爲什麼? 其實道理很簡單,如果先渲染container,怎麼找inner呢?我說了全部遍歷一遍,然後又說了如果是從右到左的話,就會少一些查找。
下面代碼,父級元素container高度是多少?
<div class="container">
<div class="inner"/>
<div class="inner"/>
</div>
.container {
border: 1px;
}
.inner {
margin: 10px;
height=width: 10px;
}
我自己重新寫了一個比較好理解的代碼,具體如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.container {
border: 1px;
}
.inner1 {
margin: 10px;
height: 10px;
width: 10px;
background: red;
}
.inner2 {
margin: 10px;
height: 10px;
width: 10px;
background: blue;
}
</style>
</head>
<body>
<div class="container">
<div class="inner1"></div>
<div class="inner2"></div>
</div>
</body>
</html>
查看結果,高度爲50px
面試官然後又添加了浮動,再問父級元素高度
<div class="container">
<div class="inner"/>
<div class="inner"/>
</div>
.container {
border: 1px;
}
.inner {
float: left;
margin: 10px;
height=width: 10px;
}
查看結果,高度爲0px,此時就出現了一個高度塌陷問題。
因爲子元素設置了浮動,導致父級元素高度沒有算進去。通過以下方式即可清楚浮動,形成一個BFC,就會加上子集元素的高度。
.container {
border: 1px;
overflow: hidden;
}
重新打開開發者工具(F12),可以看到父級元素有了高度。
100px直徑的圓你怎麼畫?
推薦:CSS3 border-radius圓角各個屬性值作用演示實例頁面
考察 JS
下面代碼會輸出什麼?(考察變量提升)
第一步
console.log(a);
此時會報錯,因爲a未定義。
第二步
console.log(a);
var a=1;
會輸出 undefined
上述代碼中,變量 a
用 var
命令聲明,會發生變量提升,即腳本開始運行時,變量 a
已經存在了,但是沒有值,所以會輸出 undefined
。
第三步
console.log(a);
var a=1;
function a(){}
上述代碼會輸出一個函數 a
考察知識點:當有多個同名變量聲明的時候,函數聲明會覆蓋其他的聲明。如果有多個函數聲明,則是由最後的一個函數聲明覆蓋之前所有的聲明。
第四步
console.log(a);
var a=1;
function a(){}
a=2;
上述代碼結果與知識點與 第三步
一樣
第五步
console.log(a);
var a=1;
function a(){}
a=2;
a();
推薦閱讀:javascript變量提升詳解
推薦閱讀:深入淺出JS - 變量提升(函數聲明提升)
輸出結果:
上述代碼,首先進行函數聲明提升,然後再進行變量提升,此時 a
已經是一個變量了,不再是一個函數了,所以就會 throw error
。
下面代碼會輸出什麼?(考察事件循環)
推薦閱讀:Javascript 事件循環event loop
第一步
setTimeout(() => {
console.log(1)
},0);
console.log(2)
// 輸出結果:2 1
第二步
setTimeout(() => {
console.log(1)
},0);
console.log(2)
new Promise((resolve) => {
console.log(3);
resolve(4)
}).then(val => console.log(val))
// 輸出結果:2 3 4 1
第三步
document.body.addEventListener('click', () => {
Promise.resolve().then(() => console.log(1))
console.log(2);
}, false)
document.body.addEventListener('click', () => {
Promise.resolve().then(() => console.log(3))
console.log(4);
}, false)
之前那幾題答完後,這道題,我毫不猶豫的直接說了 2 4 1 3 ,然後面試官給了我下面這代碼提示,啪,直接被拍醒…
document.body.click()
後面搜了相關資料,原來是設計模式中的發佈-訂閱模式,之前就看了面經,知道字節喜歡考這個設計模式,原來真的又考到了,不過題型不一樣罷了。
算法題
你瞭解迴文字符串嗎?例如abba,寫一下代碼吧(不限語言)
#include<bits/stdc++.h>
using namespace std;
int main(){
string str;
cin>>str;
int len=str.length();
int mid=(len+1)/2;
string ans1=str.substr(0,mid); //取前半段字符串
if(len&1) mid--;
string ans2=str.substr(mid,len-mid); //取後半段字符串
reverse(ans2.begin(),ans2.end()); //取反
if(ans1==ans2) cout<<1<<endl;
else cout<<0<<endl;
return 0;
}
由於不限語言,我就用了 C++
編寫了,當時忘記對第二個字符串取反了,也沒有驗證是否可行,就直接上代碼了 …
那你可以求出字符串中最長迴文子串嗎?例如給你abbac,得到abba
我當時提到了馬拉車算法(Manacher
),面試官要我手寫一下,還是吃了過去的虧啊,打比賽還遇到過,沒有怎麼使用就忘了,今天又重新溫習一下吧…
話不多說,先貼參考博客:
推薦閱讀:馬拉車算法(Manacher’s Algorithm)
貼上代碼:
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn=1e3+5;
int p[maxn];
int main(){
string str;
while(cin>>str){
string s="$";//第一步:預處理,將原字符串轉換爲新字符串
for(int i=0;i<str.length();i++)
s+="#",s+=str[i];
s+="#@"; //尾部再加上字符@,將偶數長度變爲奇數長度
int n=s.length();
int id=0,mx=0; //初始化中心位置和最右端位置
int maxlen=-1,idx=0;
for(int j=1;j<n-1;j++){
p[j]=mx>j? min(p[2*id-j],mx-j):1;
while(s.at(j+p[j]) == s.at(j-p[j])) //向左右兩邊拓展
p[j]++;
if(mx<p[j]+j){ //如果超過右邊界,進行更新
mx=p[j]+j;
id=j;
}
if(maxlen<p[j]-1){ //更新最大長度和中心索引位置
maxlen=p[j]-1;
idx=j;
}
}
int start=(idx-maxlen)/2; //求起始點索引
string ans=str.substr(start,maxlen); //字符串截取得到最長迴文子串
cout<<ans<<endl;
}
return 0;
}
用兩種數據,進行測試,得到如下結果:
總結
每次面完之後,都要好好總結一下。當我寫完這篇文章之後,我發現本次面試難度算很低了,沒有考察要你手撕各種設計模式,還有手撕 Promise
、手撕 new
等等。這些我在面試前還做了準備,但是我發現我又遺忘了…原因很簡單,只是第一次接觸那些手撕,用的也不是很多,還有更多的知識也是在面試前搶記的。看的特別廣而雜,反而知識沒有連通性,記着記着自然遺忘了。
其次,對於字節跳動,經歷過這一次面試後,我也逐漸發現面試都差不多,面試官都挺好的,期間還會暗示你(當然你懂他意思的話…),面之前,一看是頭條我一下覺得沒啥自信了,都說頭條很在意算法,但本次考察的算法好像沒有 acm
那樣的難度,而是自己把自己給嚇住了,信心自然沒了…
過了好幾天了,也沒有收到感謝信,當然,也沒有收到下次面試通知,估計是 “入庫” 了吧,在此,寫下今後的安排:
- 無需懼怕算法題,難度真的沒有
acm
那個高度 - 將知識重新撿起來,不要在要面試的時候,才臨時搶記知識
- 安靜地努力,每篇文章寫完後就要消化,不要沒弄懂就發佈
- 文章不在於多,而在於精,將每篇文章都要認真書寫,讓讀者都能一讀即懂
發現每次只有把知識點講出來纔會印象深刻,今後的日子也要這樣做!
最後,本次面經書寫完畢,算是一次不錯的體驗了,還要繼續加油~
更多前端知識內容,可以訪問我的筆記倉庫:https://yangchaoyi.vip/
製作不易,留下您的點贊 和 star,就是對我的支持啦~
學如逆水行舟,不進則退