前言
自己整理出來的一些前端工程師必備的面試題,面試中出場率很高,相信小夥伴在找工作或者跳槽中能夠順利找到自己想要的工作。
Html
1、DOCTYPE有什麼作用?標準模式與混雜模式如何區分?它們有何意義?
DOCTYPE的作用是告訴瀏覽器使用哪種版本的HTML規範來渲染文檔。當DOCTYPE不存在或者形式不正確時會導致HTML文檔以混雜模式解析文檔。標準模式以瀏覽器支持的最高標準運行,混雜模式中的頁面會以一種比較寬鬆的向後兼容的方式顯示。
H5不基於SGML因此不需要對DTD進行引用,但是需要通過DOCTYPE來規範瀏覽器行爲。HTML4.0基於SGML,所以需要引用DTD才能告知瀏覽器文檔所使用的文檔類型。
2、頁面導入樣式時,使用link和@import的區別?
1、Link屬於HTML標籤,除了能加載CSS樣式外還可以加載RSS,而@import是CSS提供,只能用於加載CSS。
2、頁面被加載時,使用link標籤的加載的CSS樣式會同時被加載,而@import引用的CSS會等到頁面被加載完再加載。
3、@import是CSS2.1提供的,只有再IE5及以上才能被識別,而link是HTML標籤,無兼容問題。
3、H5有哪些新特性?
1、語義化更好的標籤如section、article、header、nav、footer main aside
2、多媒體元素audio和video
3、繪圖功能canvas和SVG兩種繪圖方法
4、地理位置信息、移動端事件處理
5、客戶端存儲localStorage 和sectionStorage
6、動畫優化的requestAnimationFrame
7、WebWorker開闢一個獨立同時運行的線程,減少渲染時間
8、WebSocket請求,類似於HTTP協議,用於跨域通信
4、HTML5中的localStorage和sectionStorge 與cookie的異同?
相同:都是保存在瀏覽器客戶端,並且都是同源的。
不同:
1、存儲量:LocalStorage 和sessionStorage:存儲數據可達5M而cookie存儲數據很小一般爲4k
Cookie會隨着HTTP請求一同發給用戶,即cookie在瀏覽器和服務器之間來回傳遞,而localStorage和sessionStorage不會把數據發送給服務器,僅在本地保存
2、數據的有效期不同:cookie在設置的cookie過期時間內一直有效。而localStorage始終有效,即使窗口或者瀏覽器關閉數據還是長久保存。SessionStorage僅在瀏覽器窗口關閉之前有效,一旦關閉就丟失了。
3、作用域不同:cookie在所有的同源窗口都是共享的。LocalStorage在所有的同源窗口都是共享的,sessionStorage不在不同的瀏覽器共享,即使同一頁面。
5、行級元素與塊級元素的區別?
行級元素不會獨佔一行,無法設置其width和height,高度由其內容區撐開,行級元素內只能是行級元素而不能是塊級元素。Padding可以設置,但是margin-top和margin-bottom設置無效。
塊級元素獨佔一行,可以設置width和height,高度由內部元素撐開,塊級元素內可以是塊級元素也可以是行級元素。可以設置margin和padding。
6、Px與 em和 rem的區別?
Px是絕對像素值,大部分元素可以設置,如果沒有設置會繼承父元素的像素值
Em是相對單位。它是以當前元素的父級元素的基值來設置的
rem是相對單位。它是根據HTML元素的像素基值爲1rem,常用於解決PC端與移動端的自適應。
7、簡述src 與href的區別?
src用於替換當前元素。而href則是建立當前元素與外界資源的聯繫。
src指向外部資源,指向的內容將會下載並嵌入到當前標籤的位置,在請求src資源時會將其下載並應用到文檔內。例如iframe img script標籤等。
當瀏覽器解析到該元素時,會暫停其他資源的下載和處理,直到將資源加載、編譯、執行完畢纔會去處理其他元素。這也是爲什麼要將script放於底部的原因。
href標籤指向網絡資源的所在位置,建立當前元素和外部資源的鏈接。並且下載該資源時瀏覽器會並行下載並且不會停止對當前文檔的處理。所以推薦css實例link方式加載。
CSS
1、介紹一下標準的CSS盒模型?這與低版本的IE盒模型有什麼區別?
標準盒模型:寬度=內容區寬度(content)+padding+border
IE怪異盒模型:寬度=內容寬度(width+padding+border)
2、CSS如何設置這兩種盒模型?
通過box-sizing這個屬性來設置
box-sizing:content-box 爲標準盒模型
box-sizing:border-box 爲IE怪異盒模型
3、JS如何獲取盒模型對應的寬和高?
Dom.style.width/height 直接獲取元素的寬高(只能獲取內聯樣式)
Dom.currentStyle.width/height IE獨有的獲取寬高樣式
Window.getComputedStyle(dom).width/heihgt 兼容的獲取寬高樣式的方法
Dom.getBoundingClientRect().width/height //計算元素相對於viewport的位置有left top right bottom x y width height8個值
4、BFC的原理?(BFC的渲染規則)
1、在BFC元素的垂直邊距上會發生重疊
2、BFC的區域不會與浮動元素的box重疊
3、BFC在頁面上是一個獨立的容器,外面的元素不會影響到裏面的元素,裏面的元素也不會影響外面的元素。
4、計算BFC高度時浮動元素也會參與計算。
5、BFC可以阻止元素被浮動元素覆蓋。
5、如何創建BFC?
float值不爲none
overflow不爲visible
position的值不是static或者relative
display爲inline-block或者table-cell或者flex
6、BFC的常見使用場景?
1、解決邊距重疊問題
2、BFC不與float元素重疊
3、清除浮動(父級元素會計算浮動元素的高度)
7、CSS三欄佈局的實現?
1、使用float浮動元素要位於非浮動元素的上面
2、使用絕對定位的方式來實現
3、使用flex彈性盒模型來實現
4、使用table佈局來實現 父元素設置爲table佈局子元素佈局設置爲table-cell即可
5、使用grid佈局,使用grid-template-rows來設置高度使用grid-template-columns來設置列數
6、使用inline-block配合calc來實現三欄佈局
7、雙飛翼佈局
8、聖盃佈局
8、你對line-height是如何理解的?
行高是指一行文字的高度,具體是說兩行文字之間基線的距離。CSS中起作用的height和line-height,沒有定義height屬性,最終其表現作用一定是line-height
單行文本垂直居中:只需要設置height和line-height相等即可
多行文本垂直居中:不知道寬高的可以使用padding上下一致即可
1、父元素設置display:table,子元素設置display:tabel-cell,vertical-align:middle
2、父元素設置height和line-height相等,子元素設置爲display:inline-block,vertical-align:middle,line-height:16px即可
3、父元素有高度設置相對定位,子元素將該元素設置絕對定位,使其垂直居中 top:50%; margin-top:-xxpx;
9、怎麼讓chrome支持小於12px的文字?
P{font-size:10px; -webkit-transform: scale(0.8);}
10、如何使得一個元素的高度隨着它的寬度變化,且高度一直是寬度的一半?
1、元素設置width屬性,然後使用JS動態設置其寬度(但是需要時刻監聽元素的改變)
2、元素不設置高度,僅設置寬度然後padding:25% 0 此時需要設置父元素;
注意:
: padding,margin值是相對於其父元素進行設置的。
11、如何實現垂直水平居中?
大家可以參考這篇文章學習,寫的比較詳細,考慮也比較全面
12、清除浮動的方法你知道幾種?
1、父級元素增加空div通過clear:both
2、父級元素設置height
3、父級元素使用overflow:hidden/auto 觸發BFC
4、讓父級元素也浮動起來
5、將父級元素設置爲display:table佈局
13、li元素元素直接的看不見的空白怎麼消除?
1、將所以的li寫在一行上
2、使用浮動
3、設置font-size: 0;
14、display:inline-block 什麼時候會顯示間隙?
1、元素直接存在空格時,解決:消除空格
2、margin值存在時,解決:將margin值設置爲負值
3、font-size不爲0,解決:將font-size: 0;
15、什麼時候使用背景圖什麼時候使用img?
1、佔位:img圖片是佔位的,它是html的一個標籤,如果未設置大小則會按照圖片大小進行展示
background-image是不佔位的,它只是css的一個屬性
2、否可操作
background-image是隻能看的,只能設置background-position, background-attachment, background-repeat, background-size等屬性來操作圖片
img是一個document對象,它是可以操作的。比如更換img src的路徑可以達到更換圖片的目的,也可以移動它的位置,從document中移除等等操作。所以如果是裝飾性的圖片就使用background-img,如果和文體內容很相關就使用img。
3、加載順序不同
background-image 是在頁面結構加載完成之後才加載的,而img是頁面標籤會在加載結構的時候加載,如果img圖片很大那麼加載時間很長後面的結構都會被阻塞,所以實際加載順序上會加載img然後加載background-image
16、flex佈局
17、常用的預處理器語言mixin
預處理器語言大家可以自己根據喜好進行選擇,通常常用的scss,sass,less, stylus等
下面是一些我開發過程中用到的scss的mixin,希望給大家參考
// position定位相關
@mixin position($type: absolute,
$left: null,
$right: null,
$top: null,
$bottom: null) {
position: $type;
@if ($left !=null && $left !='') {
left: $left;
}
@if ($right !=null && $right !='') {
right: $right;
}
@if ($top !=null && $top !='') {
top: $top;
}
@if ($bottom !=null && $bottom !='') {
bottom: $bottom;
}
}
// 單行隱藏
@mixin ellipsis($line) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $line;
overflow: hidden;
text-overflow: ellipsis;
}
// 多行隱藏
@mixin ellipsis-one {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/*彈性盒子居中(傳入null不設置該屬性)*/
@mixin flex-center($direction:row, $justify:center, $align:center, $flex-wrap: null) {
display: flex;
@if ($direction != null) {
flex-direction: $direction;
}
@if ($justify != null) {
justify-content: $justify;
}
@if ($align != null) {
align-items: $align;
}
@if ($flex-wrap != null) {
flex-wrap: $flex-wrap;
}
// 行高
@mixin line-height($height:30px, $line-height:30px) {
@if ($height != null) {
height: $height;
}
@if ($line-height != null) {
line-height: $line-height;
}
// 清除浮動
@mixin clearfix() {
&:before,
&:after {
content: "";
display: table;
}
&:after {
clear: both;
}
}
JS
1、let、const和var的區別?
var聲明變量可以重複聲明,而let不可以重複聲明
var是不受限於塊級的,聲明會提升,而let是受限於塊級,僅在該塊級內起作用
var會與window相映射(會掛一個屬性),而let不與window相映射
var可以在聲明的上面訪問變量,而let有暫存死區,在聲明的上面訪問變量會報錯
const聲明之後必須賦值,否則會報錯
const定義不可變的量,改變了就會報錯
const和let一樣不會與window相映射、支持塊級作用域、在聲明的上面訪問變量會報錯
2、說說你使用遞歸時爲什麼會出現內存溢出的情況?
遞歸非常消耗內存,因爲需要同時保存很多的調用幀,因爲棧可存放的函數是有限制的,一旦存放了過多的函數且沒有得到釋放的話,就會出現爆棧的問題。
解決:可以使用尾遞歸解決
3、談談你認爲的event loop?
event loop是JavaScript的事件執行機制,我們都知道JS是單線程的,而且異步任務的執行時間是晚於同步任務的,這就是因爲存在event loop。
因爲JS中存在宏任務和微任務,這兩個分別維護一個隊列,都是採用先進先出的策略執行的,並且宏任務的優先級高於微任務。
常見的宏任務有:script,setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 環境)。
常見的微任務有:Promise.then、 MutationObserver、 process.nextTick(Node.js 環境)。
任務執行順序如下:
1、從宏任務的頭部取出一個任務執行,先執行宏任務;
2、執行過程中若遇到微任務則將其添加到微任務的隊列中(等執行棧空閒在執行);
3、宏任務執行完畢後,微任務的隊列中是否存在任務,若存在,則挨個兒出去執行,直到執行完畢;
4、如有必要會進行GUI 渲染;
這就是event loop的執行順序。
4、Xss 和CSRF的原理與預防?
1、
XSS,即 Cross Site Script,中譯是跨站腳本攻擊;其原本縮寫是 CSS,但爲了和層疊樣式表(Cascading Style Sheet)有所區分,因而在安全領域叫做 XSS。
XSS 攻擊是指攻擊者在網站上注入惡意的可執行代碼,通過惡意腳本對客戶端網頁進行篡改,從而在用戶瀏覽網頁時,控制用戶或者獲取用戶隱私數據的一種攻擊方式。
XSS主要分爲3種(非持久型,持久型,基於DOM型)
1、非持久型主要是通過攻擊者在網站中注入惡意代碼,如URL中注入script腳本或者注入能夠獲取用戶信息的腳本,從而達到攻擊的目的。
通常這種攻擊是一次性的,惡意腳本並不會被寫入數據庫中,所以相對來說危害不是很大。
2、持久型用戶輸入的惡意代碼會被存儲在服務器端,當瀏覽器請求數據時,腳本從服務器上傳回並執行。這種 XSS攻擊具有很強的穩定性,而且攻擊到的是很多用戶。
比較常見的一個場景是攻擊者在社區或論壇上寫下一篇包含惡意 JavaScript 代碼的文章或評論,文章或評論發表後,所有訪問該文章或評論的用戶,都會在他們的瀏覽器中執行這段惡意的 JavaScript 代碼。
3、基於DOM的則是發生在客戶端,攻擊者惡意修改dom結構而造成的攻擊
防禦:1、對用戶輸入的任何東西進行字符轉義,永遠不要相信用戶的輸入 2、使用httpOnly防止cookie被劫持
2、
CSRF 攻擊是攻擊者藉助受害者的Cookie騙取服務器的信任,可以在受害者毫不知情的情況下以受害者名義僞造請求發送給受攻擊服務器,從而在並未授權的情況下執行在權限保護之下的操作。
滿足條件:用戶已經登陸, 網站存在安全漏洞
防禦:1、使用token進行驗證,每次發送請求是都要驗證token值是否正確,這樣可以防禦惡意攻擊者的請求攻擊。2、Referer驗證,通過檢查referee來確定請求是否是合法的源。 3、設置SameSite,可以對 Cookie 設置 SameSite 屬性。該屬性表示 Cookie不隨着跨域請求發送,可以很大程度減少 CSRF 的攻擊,但是該屬性目前並不是所有瀏覽器都兼容
5、前後端如何通信?
1、Ajax 同源下的通信方式
2、websocket不受同源策略的限制
2、CORS支持跨域通信也支持同源通信
6、跨域通信的幾種方法?
1、JSONP
原理:利用script標籤的異步加載。前後端約定callback函數名,後端返回一個函數名包含的數據對象。需要後端的配合。 JSONP 使用簡單且兼容性不錯,但是只限於 get 請求。
2、document.domain
方式只能用於二級域名相同的情況下,比如 x.test.com 和 y.test.com 適用於該方式。 只需要給頁面添加 document.domain = ‘test.com’ 表示二級域名都相同就可以實現跨域
3、iframe和location.hash
原理:hash改變頁面不刷新,可以通過改變hash值來執行回調操作從而實現數據的傳遞
4、postMessage
這種方式通常用於獲取嵌入頁面中的第三方頁面數據。一個頁面發送消息,另一個頁面判斷來源並接收消息,通過postMessage方法和window.onmessage方法實現
5、Websocket
原理:不受同源策略的限制
6、CORS
原理:後端服務器開啓 Access-Control-Allow-Origin 即可以啓動CORS,執行某個域下可以通信。
7、服務器代理
原理:通過服務器做第三方代理,來傳遞請求和接受返回的數據並進行傳遞過去。
7、性能優化的方案有哪些?
借一張圖片來看,大家如果想看的話也可以去看看這本小冊前端性能優化原理與實踐
8、HTTP協議的主要特點?
簡單快速:每個資源URI是固定的,在HTTP協議中處理比較簡單
靈活:HTTP中頭部分數據類型,通過一個HTTP協議可以完成不同數據類型的傳輸
無連接:連接依次就會斷掉不會保持連接
無狀態:客戶端與服務端是兩種身份,HTTP幫忙建立連接,依次任務完成就斷開,下次傳輸還是需要確立連接
9、HTTP報文的組成部分?
請求報文:請求行、請求頭、空行、請求體
響應報文:狀態行、響應頭、空行、響應體
10、POST和GET請求的區別?
GET在瀏覽器回退時是無害的,而POST會再次提交
GET產生的URL地址可以被收藏,而POST不可以
GET請求會被瀏覽器主動緩存,而POST不會除非主動設置
GET只能進行url編碼,而POST支持多種編碼
GET請求參數會被完整的保留在瀏覽器歷史記錄中而POST的參數不會
GET請求在URL傳送的參數的長度有限,而POST沒有限制
GET只能接受ASCII字符,而POST沒有限制
GET比POST更不安全,因爲參數直接暴露在URL上,所以不安全
GET參數通過URL傳遞,而POST放在Request body中
11、常見的HTTP狀態碼?
1XX:指示信息–表示信息已被接受,繼續處理
2XX:成功 --表示信息已經成功返回
200 OK客戶端請求成功
206 Partial Content:客戶端發送了一個帶RangeGET請求,服務器響應完成
3XX: 重定向 --表示資源地址已經變更
301 永久重定向 所有請求的頁面已經轉移之新的URL
302 所請求的頁面已經臨時轉移至新的URL
304請求已經發出,但是爲滿足要求而不需要進行請求即可完成
4XX:請求錯誤–表示客戶端請求出錯
401 用戶未登錄或者爲授權
403 Forbidden 對請求的頁面的訪問被禁止
404 Not Found 請求的資源不存在
5XX:服務器端出錯 --表示服務器正在維修或者已壞
500:服務器發生不可預期的錯誤原來緩衝的文檔還可以繼續使用
503:請求未完成,服務器臨時過載或當機,一段時間內可恢復正常
12、http的緩存機制是怎麼樣的?
HTTP 緩存是我們日常開發中最爲熟悉的一種緩存機制。它又分爲強緩存和協商緩存。優先級較高的是強緩存,在命中強緩存失敗的情況下,纔會走協商緩存。
返回狀態碼是200 from xxxx
1、expires,這個的實現機制是expires是一個時間戳,接下來如果我們試圖再次向服務器請求資源,瀏覽器就會先對比本地時間和 expires 的時間戳,如果本地時間小於 expires 設定的過期時間,那麼就直接去緩存中取這個資源。
那麼問題就很明顯了,這個非常依賴本地時間,如果用戶修改了本地時間那麼緩存機制也就失效了。
2、cache-control,在 Cache-Control 中,我們通常通過 max-age 來控制資源的有效期。max-age 不是一個時間戳,而是一個時間長度,如max-age=33433000;這樣的話只有這個請求在這個時間段內都是有效的。並且cache-control優先級也更高,也向下兼容。
返回的狀態碼是304 Not Modified
1、Last-Modified與If-Modified-Since
工作:服務器接收到請求過來的If-Modified-Since時會對比這個時間與服務器文件修改的最後時間,如果發生變化則會返回一個完整的響應,否則會返回304,並且響應頭中不會有Last-Modified字段。
但是也存在缺陷:
1、我們如果對資源進行了編輯,但是內容並未修改,而修改時間卻改變了,那麼下一次請求時需要重新響應,但是返回的數據卻並未改變。
2、如果我們修改文件的時間很短(s以內),由於 If-Modified-Since 只能檢查到以秒爲最小計量單位的時間差,那麼這個他的Last-modified並未修改,導致請求的資源反而出錯。
2、Etag 是由服務器爲每個資源生成的唯一的標識符,這個標識符是基於文件內容生成的,只要文件內容不同,它們對應的 Etag 就是不同的。
因此每次請求只需要對比Etag是否改變即可。Etag的優先級更高,並且兩者同時存在是優先使用Etag。
13、談談你對原型鏈的理解?
每一個對象中都存在__proto__屬性,__proto__屬性裏面又存在constructor函數,函數內部又有prototype,並且這個值和__proto__屬性一樣。所以有這樣的關係:
那麼原型鏈是怎麼回事呢?
原型鏈就是多個對象通過__ proto __ 的方式連接了起來。噹噹前元素中沒有某一個函數或者方法時會沿着原型鏈一直往上查找,直到最頂部爲止。
大家可以理解下這張圖片
14、instanceOf的原理?
原理:本質上通過原型鏈來判斷的
// instanceof 原理實現
function myInstanceof (left, right) {
let prototype = right.prototype
let left = left.__proto__
while (true) {
if (left === null || right === null) {
return false
}
if (prototype === left) {
return true
}
left = left.__proto__
}
}
15、new運算符工作原理?
new構造四部曲:
1、生成一個obj對象
2、鏈接原型
3、綁定this
4、返回對象
function create () {
let obj = {}
let Con = [].shift.call(arguments)
obj.__proto__ = Con.prototype
let result = Con.apply(obj, arguments)
return result instanceof Object ? result : obj
}
可以思考下這道題:new關鍵字的問題(return 1後會是什麼情況)?
16、繼承的方法你知道幾種?
1、藉助構造函數實現繼承
原理:將父類構造函數的this指向子類構造函數的實例上去
缺點:繼承的子類上沒有繼承到父元素原型上的屬性和方法,並沒有實現真正的繼承,只是實現了部分繼承。
2、藉助原型鏈實現繼承(解決了繼承到父元素到方法問題)
原理:子類構造函數的prototype鏈接到父類實例對象上實現原型鏈的繼承
缺點:原型對象是共用的,子類構造函數構造出來的實例對象改變原型對象的屬性時會導致另一個繼承子類的實例對象的屬性隨之改變。原因是因爲子類構造出來的子類執行的原型對象共用同一個地址。
3、組合方式 上面兩種方式結合 (解決了公用地址問題)
缺點:子類構造函數的原型對象沒有自己的constructor,所以會網上找到父類原型對象的構造函數,所以子類的constructor會指向父類的構造函數,並且父類的構造函數被調用了兩次
function Parent (name) {
this.name = name;
this.color = ['red', 'blue'];
}
Parent.prototype.sayName = function () {
console.log(this.name);
}
function Son (name, age) {
Parent.call(this, name);
this.age = age;
}
Son.prototype = new Parent();
Son.prototype.sayAge = function () {
console.log(this.age);
}
let son = new Son('liming', 20);
let son2 = new Son('saner', 22);
console.log(son.name) // liming
console.log(son.sayAge()) // 20
console.log(Son.constructor === Parent.constructor) // true
4、組合繼承優化 (解決以上兩個問題)
function Parent (name) {
this.name = name;
this.color = [1, 2];
}
Parent.prototype.sayName = function () {
console.log(this.name);
}
function Son (age) {
Parent.call(this, name);
this.age = age;
}
Son.prototype = Object.create(Parent.prototype); // 通過添加中間橋樑實現
Son.prototype.constructor = Son; // 將自己的構造函數指回自己
Son.prototype.sayAge = function () {
console.log(this.age);
}
let son = new Son('liming', 20);
let son2 = new Son('saner', 22);
console.log(son.constructor); // function Son() {....}
5、聖盃繼承方式
function inherit(target, origin) {
function F() {};
F.prototype = origin.prototype;
target.prototype = new F();
target.prototype.constructor = target;
target.prototype.uber = origin.prototype;
}
17、Call、apply的區別?
相同:都是改變this指向
不同點:傳參列表不同,call是變量傳入,apply是數組方式傳入
18、This指向問題?
1、對於函數直接調用的方式,不管在何處被執行,this一定指向window。
2、函數的執行時有調用者,那麼誰調用this就指向誰
3、通過 new 的方式,this 永遠被綁定在新創建的對象上,任何方式都改變不了 this 的指向。
4、通過call,apply改變this指向的時候,this指向傳入的第一個參數值。
5、箭頭函數執行時,this是包裹箭頭函數最近的一個普通函數內的this
19、閉包是什麼原理?優缺點?
函數A裏面包含了函數B,而函數B裏面引用了函數A的變量,那麼函數B被稱爲閉包。
或者:閉包就是能夠讀取其他函數內部變量的函數。
閉包存在的意義就是讓我們可以間接訪問函數內部的變量
一個是可以讀取函數內部的變量,並且這個變量不會被回收
另一個是封裝對象的私有屬性和私有方法
消耗內存,可能會造成內存溢出的情況。
閉包經典題:
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, 1000)
}
以上這段代碼的輸出
由於setTimeouts是個異步函數,所以會先把循環全部執行完畢,最後纔會打印輸出6個6
如何解決
第一種方法 使用立即執行函數解決
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 1000)
} (i));
}
第二種方法 使用let的塊級作用域解決
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000)
}
第三種方法 使用setTimeout的第三個參數,這個參數會被當成 timer 函數的參數傳入
for (var i = 1; i <= 5; i++) {
setTimeout(function (i) {
console.log(i);
}, 1000, i)
}
20、服務器如何與客戶端實現長連接(如何識別客戶端)?
HTTP協議採用‘請求-應答’模式,當採用普通模式,即非keep-alive模式時,每個請求/應答客戶端和服務器端都要重新建立一個連接,完成之後立即斷開連接(HTTP是無連接的協議)
持久連接時當使用keep-alive模式時,keep-alive功能使客戶端到服務器端的連接持久有效,當出現對服務器的後繼請求時,keep-alive公告避免了建立或者重新建立連接。
21、事件的冒泡與捕獲機制?
一個完整的事件流分三個階段,第一階段時捕獲,第二階段是目標階段-即事件通過捕獲到達目標階段,第三階段是冒泡階段即從目標元素上傳到window對象。
Vue
1、生命週期鉤子函數(在特定時刻運行的函數)
每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做生命週期鉤子函數。
如上圖所示
created中適合發送請求。
mounted階段dom元素已經掛載,可以操作dom元素
updated函數可以根據更新後執行一系列操作
beforeDestroy可以用於移除事件、定時器等等,否則可能會引起內存泄露的問題。
destroyed進行組件的銷燬操作,如果有子組件也會遞歸銷燬,最後銷燬父組件。
還有keep-alive的兩個獨有生命週期,分別爲 activated 和 deactivated,用keep-alive包裹的組件在切換時不會銷燬而且緩存到內存中並執行deactivated,命中緩存渲染時執行activated函數。
2、v-show和v-if的區別?
v-show是切換display屬性來達到顯示與隱藏的效果的。這也就是說v-show剛開始一定會加載,有比較高的初始渲染開銷,但是一旦渲染後就不需要銷燬組件,所以更適合切換場景頻率高的。
v-if是惰性的,如果剛開始爲false的話那麼就不會掛載組件,直到條件爲true是纔會去掛載渲染組件。並且在切換是會觸發銷燬/掛載組件,切換開銷比較大,適合不經常切換的場景。當然這種惰性渲染也可以減少頁面初始渲染開銷。
3、computed和watch有什麼區別?
computed常用於計算值的場景
計算屬性是基於它們的響應式依賴進行緩存的,所以當值沒發生改變時時不會執行的
還可以使用setter函數來處理一些邏輯
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
watch用來觀察和響應 Vue 實例上的數據變動,實時對數據監聽並處理一些複雜的邏輯操作
無緩存性,頁面重新渲染時值不變化也會執行
常見的監聽使用方式:
watch: {
// 該回調會在任何被偵聽的對象的 property 改變時被調用,不論其被嵌套多深
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 該回調將會在偵聽開始之後被立即調用
d: {
handler: 'someMethod',
immediate: true
},
//e: {
// f: {
// g: 5
// }
//}
e: [
'handle1',
function handle2 (val, oldVal) { /* ... */ },
{
handler: function handle3 (val, oldVal) { /* ... */ },
/* ... */
}
],
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
}
4、如何動態綁定class和style?
1、綁定class
使用對象來綁定:<div v-bind:class="{ active: isActive }"></div>
使用數據綁定多個:<div v-bind:class="[activeClass, errorClass]"></div>
2、綁定style
使用對象來綁定<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
使用數組綁定多個<div v-bind:style="[baseStyles, overridingStyles]"></div>
5、key的作用?
爲了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要爲每項提供一個唯一 key 屬性。以便於在diff是可以快速查找到某個元素。
6、keep-alive 組件有什麼作用?
如果你在組件切換的時候,需要保存一些組件的狀態防止多次渲染,就可以使用 keep-alive 組件包裹需要保存的組件。
用 keep-alive 包裹的組件在切換時不會進行銷燬,而是緩存到內存中並執行 deactivated 鉤子函數,命中緩存渲染後會執行 actived 鉤子函數。
7、組件通信的方式有多少種?
1、父子組件
2、非父子組件
1、使用props向子組件傳遞數據,子組件通過emit觸發事件向父組件傳遞數據
2、父組件通過ref調用子組件的方法
3、通過.sync語法實現數據的父子組件的通信
1、provide 選項允許我們指定我們想要提供給後代組件的數據/方法,使用provide注入,子組件中通過inject接受即可使用
2、Event Bus 解決,在Vue.prototype上面掛載bus總線,使用bus的emit和on事件完成
3、使用vuex來實現跨組件通信。
8、組件中 data 爲什麼是個函數而根組件中卻是對象?
組件複用時所有組件實例都會共享一個 data,如果 data 是對象的話,就會造成一個組件修改 data 以後會影響到其他所有組件,所以需要將 data 寫成函數,每次用到就調用一次函數獲得新的數據。而new Vue的實例,是不會被複用的,因此不存在對象引用問題。
9、Vue雙向綁定的實現?
原理:
unction observe(obj) {
// 判斷類型
if (!obj || typeof obj !== 'object') {
return
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, key, val) {
observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// get函數
get: function reactiveGetter() {
console.log('get')
return val
},
// set函數
set: function reactiveSetter(newVal) {
console.log('set')
val = newVal
}
})
}
10、mixin 和 mixins 區別?
mixin 用於全局混入,會影響到每個組件實例,通常插件都是這樣做初始化的。
mixins應該是來分發 Vue 組件中的可複用功能。如果多個組件中有相同的業務邏輯,就可以將這些相同的邏輯剝離出來,通過 mixins 混入代碼,但是要注意合併的一些規則等。
11、nextTick函數有什麼作用?
nextTick可以讓我們在下次 DOM 更新循環結束之後執行延遲迴調,用於獲得更新後的 DOM。
當我們在mounted中操作ref時就可以在nextTick中執行。
// DOM 還未更新
this.nextTick(function () {
// DOM 已經更新
this.$refs.$el.xxx
})
12、單頁面與多頁面的優缺點?
13、對於Vue的優化方案?
1、區分使用v-if和v-show的使用
2、區分computed和watch的使用
3、v-for的使用時必須加上key值。
4、事件,定時器的銷燬操作
5、圖片資源的懶加載(vue插件)
6、路由的懶加載
7、第三方插件的按需引入如Echarts
8、組件的異步加載
9、服務端渲染
10、使用alias來簡化路徑查找
11、loader配置使用exclude來快速查找文件
14、什麼是VNode?
Virtual DOM 其實就是一棵以 JavaScript 對象(VNode 節點)作爲基礎的樹,用對象屬性來描述節點,實際上它只是一層對真實 DOM 的抽象。最終可以通過一系列操作使這棵樹映射到真實環境上。
15、Vue中的服務器代理如何設置?
vue-cli3.0以後都是在根目錄下新建vue.config.js來配置對應需要修改的配置的。
代理設置:
devServer: {
port: 8080,
open: true,
proxy: {
'/api/login': {
target: 'http://localhost:8081',
changeOrigin: true,
pathRewrite: {
'^/api': '/api/4356'
}
}
}
}
Git
1、Git pull操作相當於什麼?
git pull相當於git fetch + git merge
git fetch是將遠程主機的最新內容拉到本地,用戶在檢查了以後決定是否合併到工作本機分支中。
而git pull 則是將遠程主機的最新內容拉下來後直接合並,即:git pull = git fetch + git merge,這樣可能會產生衝突,需要手動解決。
2、git merge和git rebase的區別?
git merge是git會自動根據兩個分支的共同祖先的這個 commit 和兩個分支的最新提交 進行一個三方合併,然後將合併中修改的內容生成一個新的 commit。並且合併最新的commit是在合併的分支上。
如果要master合併test分支,那麼就在master上使用git merge test即可
git rebase,衍合在當前分支上重演另一個分支的歷史,提交歷史是線性的。 本質上,這是線性化的自動的 cherry-pick,會將兩個分支合併爲一個分支。
如果要將test分支合併到master上,那麼要在test上使用git rebase master
個人覺得寫的不錯的文章git merge 與 git rebase的區別
3、git commit --amend的用法?
git commit -m '描述’提交之後,發現-m的說明文字寫的有問題,想要重新寫一次,也就是想撤銷上次的提交動作,重新提交一次,則可以使用這個操作。
4、git reset回滾操作?
reset命令把當前分支指向另一個位置,並且有選擇的變動工作目錄和索引。也用來在從歷史倉庫中複製文件到索引,而不動工作目錄。
如果不給選項,那麼當前分支指向到那個提交。如果用–hard選項,那麼工作目錄也更新,如果用–soft選項,那麼都不變。
5、查看git的提交歷史?
git log 命令可以顯示所有提交過的版本信息。
git reflog 可以查看所有分支的所有操作記錄(包括已經被刪除的 commit 記錄和 reset 的操作)
6、git提交流程?
1、當本地文件修改後使用git add file將文件放入暫存區
2、git commit 給暫存區域生成快照並提交到倉庫中。
3、git push將本地倉庫的代碼同步的遠程倉庫中去。
4、git reset – files 用來撤銷最後一次git add files,你也可以用git reset 撤銷所有暫存區域文件。–soft不撤銷工作區代碼,–hard撤銷工作區代碼。
5、git checkout – files 把文件從暫存區域複製到工作目錄,用來丟棄本地修改。
Linux
1、查殺進程?
1、lsof -i tcp:端口號 查看當前進程的信息
kill pid 殺掉某個pid的進程服務
2、ps -ef | grep 8099
kill -9 PID 強行殺掉某個pid的進程服務
3、ps -ef | grep server.js
查找server.js的進程號
4、開啓一個守護進程
pm2 start app.js --env production --name test
2、文件重命名操作?
mv可以用來修改單文件改名操作 mv file1 file2 將file1更改爲file2
mv還具備文件移動功能
mv b sm/ 將文件(夾)b 移動到當前目錄下的sm目錄下
mv (-f如果目標存在直接強行覆蓋 -b移動時爲目標創建一個備份 -i目標存在會交互提醒是否要覆蓋) 源文件 目標文件
mv b sm/c 將文件(夾)b移動到當前目錄下的sm目錄下並重命名爲c
ename 批量更改文件名 rename test zhangsan test? 將test*的文件改爲zhangsan*
3、文件壓縮解壓縮?
tar -czvf test.tar ./* 打包當前文件下的文件成爲test.tar文件
tar -xzvf test.tar 將test.tar內的打包文件解壓縮至原來的地方
4、ls -l /proc/70681 查看當前的PID服務在哪個文件夾下面
5、ps -a 使用PS指令查詢進程
6、ps (-e選擇所以進程 -f列出完整列表 -a顯示由同一個TTY -x選擇進程且不含控制的TTY)
7、nohup npm run dev & 以忽略掛起信號方式允許前端服務器
8、啓動後臺服務:nohup node app.js & 後臺運行----開啓後臺服務進程,並且關機服務也不會停
9、啓動前臺服務:nohup node app.js --開啓前臺服務,關機則停止
Hr面
這些問題也是我比較常見問到的。嗯,答案我也沒有標準的,只能將問題給大家參考。
1、爲什麼選擇我們公司?
2、你覺得你比別人的優勢在哪?
3、你職業規劃是怎樣的呢 ?
4、你平時的怎麼學習的?
5、你爲啥要學前端?
6、你對前端工程師這個職位是怎麼樣理解的?
個人提問
1、目前前端部門常用的技術棧是什麼?
2、公司是否會有技術分享交流活動?
3、公司技術團隊的架構和人員組成?
4、公司的業務方向是怎麼樣的呢?
5、你認爲你們公司的優勢是什麼?
最後
各位覺得文章還行的可以點個贊激勵我繼續前行,另外有任何問題也可以評論區交流。有錯誤也歡迎大家指正。