記錄和匯總看到的和碰到的前端面試題
主要來(lái)源:
https://github.com/Advanced-Frontend/Daily-Interview-Question
HTML + CSS 篇
Q1、 常用的行內(nèi)元素、塊級(jí)元素、空元素分別有哪些?
1. 塊級(jí)元素:div ul ol li h1~h6 p table 等
2. 行內(nèi)元素: a span img input select 等
3. 空元素: br hr link等
Q2、HTML語(yǔ)義化的理解
簡(jiǎn)單來(lái)說(shuō),就是合適的標(biāo)簽做合適的事情
具體的好處有:
- 有助于構(gòu)建良好的HTML結(jié)構(gòu),有利于搜索引擎的建立索引、抓取,利于SEO
- 有利于不同設(shè)備的解析
- 有利于團(tuán)隊(duì)的開(kāi)發(fā)、維護(hù)
Q3、 常見(jiàn)的瀏覽器內(nèi)核及理解
- Trident 內(nèi)核: IE
- Gecko內(nèi)核: NETSCAPE6及以上版本,火狐
- Presto內(nèi)核:Opera7及以上 [ Opera內(nèi)核原為:Presto, 現(xiàn)為:Blink ]
- Webkit內(nèi)核:Safari、Chrome等 [ Chrome:Blink(Webkit的分支) ]
瀏覽器內(nèi)核又可以分為兩部分:渲染引擎和JS引擎。<br />
渲染引擎主要負(fù)責(zé)取得頁(yè)面的內(nèi)容、整理訊息、計(jì)算網(wǎng)頁(yè)的顯示方式等;<br />
JS引擎則是解析JavaScript語(yǔ)言,執(zhí)行Javascript語(yǔ)言來(lái)實(shí)現(xiàn)網(wǎng)頁(yè)的動(dòng)態(tài)效果。
Q4、src 與 href 的區(qū)別
區(qū)別在于 src用于替代這個(gè)元素,而href用于建立這個(gè)標(biāo)簽與外部資源之間的關(guān)系。<br />
<link href="style.css" rel="stylesheet" />瀏覽器加載到這里的時(shí)候,html的渲染和解析不會(huì)暫停,css文件的加載是同時(shí)進(jìn)行的 <br />
<script src="script.js"></script>當(dāng)瀏覽器解析到這句時(shí),頁(yè)面的加載和解析會(huì)暫停直到瀏覽器拿到和執(zhí)行完這個(gè)JS文件。
Q5、CSS中l(wèi)ink 和 import 的區(qū)別
link屬于XHTML標(biāo)簽,而@import完全是CSS提供的另一種方式,只能加載CSS<br />
加載順序不同:當(dāng)一個(gè)頁(yè)面被加載時(shí),link引用的css會(huì)被同時(shí)加載,而import引用的css會(huì)等到頁(yè)面全部被下載完再被加載<br />
兼容性的區(qū)別:由于import時(shí)CSS2.1提出的所以老的瀏覽器不支持,而link標(biāo)簽無(wú)此問(wèn)題<br />
當(dāng)使用JavaScript控制DOM去改變樣式時(shí),只能用link標(biāo)簽,因?yàn)锧import不是dom可以控制的
Q6、CSS部分優(yōu)化、提高性能的方法有哪些?
- 移除空的css規(guī)則
- 正確使用display屬性
- 不濫用浮動(dòng)、web字體
- 不聲明過(guò)多的font-size
- 不在選擇器中使用ID標(biāo)識(shí)符
- 遵守盒模型規(guī)則
- 盡量減少頁(yè)面重排、重繪
- 提取公共樣式,減少代碼量
Q7、清除浮動(dòng)的方式
浮動(dòng)的元素是脫離文檔標(biāo)準(zhǔn)流的,如果不清除浮動(dòng),就會(huì)造成父元素高度塌陷,影響頁(yè)面布局
清除浮動(dòng)的方式有:
- 為父元素設(shè)置高度
- 為父元素設(shè)置
overflow: hidden - 偽元素
.father::after {
content: "";
display: block;
clear: both;
}
注:
- 使用偽元素的好處:不增加冗余的DOM節(jié)點(diǎn),符合語(yǔ)義化
-
overflow: hidden;可以觸發(fā)BFC機(jī)制
Q8、介紹BFC及其應(yīng)用
BFC就是塊級(jí)格式上下文,是頁(yè)面盒模型布局中的一種CSS渲染模式,創(chuàng)建了BFC的元素就相當(dāng)于一個(gè)獨(dú)立的盒子,它規(guī)定了內(nèi)部如何布局,里面的元素和外面的元素互不影響。 計(jì)算BFC的高度時(shí),浮動(dòng)元素也參與計(jì)算。
BFC的特性:
- 內(nèi)部box會(huì)在垂直方向上,一個(gè)接一個(gè)的放置
- Box垂直方向的距離由margin決定,在一個(gè)BFC中,兩個(gè)相鄰的塊級(jí)元素的垂直外邊距會(huì)產(chǎn)生重疊
- 在BFC中,每個(gè)盒子的左外邊緣(margin-left)會(huì)觸碰到容器的左邊緣(border-left)(對(duì)于從右到左的格式來(lái)說(shuō),則觸碰到右邊緣)
- 形成了BFC的區(qū)域不會(huì)與float box 重疊
- 計(jì)算BFC的高度時(shí),浮動(dòng)元素也參與計(jì)算
創(chuàng)建BFC的方式有:
- html根元素
- float
- 絕對(duì)定位
- overflow 不為visiable
- display為表格布局或彈性布局
BFC主要的作用:
- 清除浮動(dòng)
- 防止同一BFC容器中的相鄰元素之間的外邊距重疊的問(wèn)題
Q9、BFC、IFC、GFC 和 FFC
BFC( Block Formatting Contexts ):塊級(jí)格式上下文
頁(yè)面上的一個(gè)隔離的渲染區(qū)域,可觸發(fā)BFC的元素有:float、position、overflow、display: table-cell;/inline-block; 作用有實(shí)現(xiàn)多欄布局等;
IFC(Inline Formatting Contexts):內(nèi)聯(lián)格式上下文
IFC的line box(線框)高度由其包含行內(nèi)元素中最高的實(shí)際高度計(jì)算而來(lái)(不受到豎直方向的padding和margin影響), IFC中的line box 一般左右都貼緊整個(gè)IFC,但是會(huì)因?yàn)閒loat元素而打亂。float元素會(huì)位于IFC 與 line box 之間,使得line box 寬度縮小。同時(shí),IFC下的多個(gè)line box 高度會(huì)不同,IFC中是不可能有塊級(jí)元素的,當(dāng)插入塊級(jí)元素時(shí),會(huì)產(chǎn)生兩個(gè)匿名塊與div隔開(kāi)(即產(chǎn)生兩個(gè)IFC,每個(gè)IFC對(duì)外表現(xiàn)為塊級(jí)元素,與div垂直排列)。作用一般有:水平居中:當(dāng)一個(gè)塊要在環(huán)境中水平居中時(shí),設(shè)置其為inline-block 則會(huì)在外層產(chǎn)生IFC,通過(guò)text-align則可以使其水平居中。垂直居中:創(chuàng)建一個(gè)IFC,用其中一個(gè)元素?fù)伍_(kāi)父元素的高度,然后設(shè)置其vertical-align: middle; 其他行內(nèi)元素就可以在此父元素下垂直居中。
GFC (GridLayout Formatting Contexts):網(wǎng)格化布局格式上下文
當(dāng)為一個(gè)元素設(shè)置display: grid; 的時(shí)候,此元素將會(huì)獲得一個(gè)獨(dú)立的渲染區(qū)域,我們可以通過(guò)在網(wǎng)格容器(grid container)上定義網(wǎng)格定義行(grid definition rows)和網(wǎng)格定義列(grid definition columns)屬性和在網(wǎng)格項(xiàng)目(grid item)上定義網(wǎng)格行(grid rows)和網(wǎng)格列(grid columns)為每一個(gè)網(wǎng)格項(xiàng)目定義位置和空間。與table同為二維的表格,GridLayout會(huì)有更加豐富的屬性來(lái)控制行和列,控制對(duì)齊以及更為精細(xì)的渲染語(yǔ)義和控制。、
FFC (Flex Formatting Contexts):自適應(yīng)格式上下文
diaplay為 flex 或 inline-flex 的元素將會(huì)生成自適應(yīng)容器(flex container),可惜這個(gè)屬性只有谷歌和火狐支持(移動(dòng)端夠用了)。Flex Box 由伸縮容器和伸縮項(xiàng)目組成。通過(guò)設(shè)置元素的display為flex 或 inline-flex 就可以得到一個(gè)伸縮容器。設(shè)置為flex的容器被渲染成一個(gè)塊級(jí)元素,而設(shè)置inline-box的元素被渲染成一個(gè)行內(nèi)元素。伸縮容器中的每一個(gè)子元素都是一個(gè)伸縮項(xiàng)目。伸縮項(xiàng)目可以是任意數(shù)量的。伸縮容器內(nèi)外元素互不影響。簡(jiǎn)單的說(shuō)就是,F(xiàn)lex Box 定義了伸縮容器里的伸縮項(xiàng)目怎么布局。
Q10、怎么讓不定寬高的DIV水平垂直居中
<div class="parent">
<div class="child"></div>
</div>
- 定位的方式
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
- CSS3屬性
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- flex 布局
.parent {
display: flex;
align-items: center;
justify-content: center;
}
Q11、分析比較opacity: 0; visibility: hidden; 和 display: none; 的優(yōu)劣
- 結(jié)構(gòu)
display: none; 會(huì)讓元素完全從渲染樹(shù)中消失,渲染的時(shí)候不占據(jù)任何空間,不能點(diǎn)擊;
visibility: hidden; 不會(huì)讓元素從渲染樹(shù)中消失,渲染元素繼續(xù)占據(jù)空間,只是內(nèi)容看不見(jiàn),不能點(diǎn)擊;
opacity: 0; 不會(huì)讓元素從渲染樹(shù)消失,渲染元素繼續(xù)占據(jù)空間,只是內(nèi)容看不見(jiàn),可以點(diǎn)擊; - 繼承性
display: none;和 opacity: 0; 是非繼承屬性,子孫節(jié)點(diǎn)消失是由于元素從渲染樹(shù)消失造成的,通過(guò)修改子孫節(jié)點(diǎn)的屬性無(wú)法將其顯示;
visibility: hidden; 是繼承屬性,子孫節(jié)點(diǎn)消失是由于繼承了hidden屬性,通過(guò)給子孫節(jié)點(diǎn)設(shè)置visibility: visible;可以將子孫節(jié)點(diǎn)顯示出來(lái); - 性能
display: none; 修改元素會(huì)造成文檔回流,性能消耗較大;
visibility: hidden; 修改元素只會(huì)造成重繪,性能消耗較少;
opacity: 0; 修改元素會(huì)造成重繪,性能消耗較少; - 聯(lián)系
三者都可以讓元素不可見(jiàn)
Q12、用CSS或JS實(shí)現(xiàn)多行文本溢出省略效果(考慮兼容性)
- CSS
*單行*
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
*多行*
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; // 行數(shù)
overflow: hidden;
text-overflow: ellipsis;
- JS
實(shí)現(xiàn)思路:
- 使用split + 正則表達(dá)式將單詞與單個(gè)文字切割出來(lái)存入words
- 加上 '...'
- 判斷scrollHeight與clientHeight,超出的話就從words中pop一個(gè)出來(lái)
<p>測(cè)試文字,test words 1231231121</p>
const p = document.querySelector('p');
let words = p.innerHTML.split(/(?<=[\u4e00-\u9fa5])|(?<=\w*?\b)/g);
while(p.scrollHeight > p.clientHeight) {
words.pop()
p.innerHTML = words.join('') + '...'
}
JavaScript 篇
Q1、數(shù)組對(duì)象有哪些常用的方法
修改器方法
-pop(): 刪除數(shù)組的最后一個(gè)元素,并返回這個(gè)元素
-push(): 在數(shù)組的末尾增加一個(gè)或多個(gè)元素,并返回?cái)?shù)組的新長(zhǎng)度
-reverse(): 顛倒數(shù)組中元素的排列順序
-shift(): 刪除數(shù)組的第一個(gè)元素,并返回這個(gè)元素
-unshift(): 在數(shù)組的開(kāi)頭增加一個(gè)或多個(gè)元素,并返回?cái)?shù)組的新長(zhǎng)度
-sort(): 對(duì)數(shù)組元素進(jìn)行排序,并返回當(dāng)前數(shù)組
-splice(): 在任意的位置給數(shù)組添加或刪除任意個(gè)元素
訪問(wèn)方法
-concat(): 返回一個(gè)由當(dāng)前數(shù)組和其它若干個(gè)數(shù)組或非數(shù)組值組合而成的新數(shù)組
-join(): 連接所有數(shù)組元素組成一個(gè)字符串
-slice(): 抽取當(dāng)前數(shù)組中的一段元素組合成一個(gè)新數(shù)組
-indexOf(): 返回?cái)?shù)組中第一個(gè)與指定值相等的元素的索引,如果找不到這樣的元素則返回-1
-lastIndexOf(): 返回?cái)?shù)組中最后一個(gè)(從右邊數(shù)第一個(gè))與指定值相等的元素的索引,如果找不到這樣的元素則返回-1
迭代方法
-
forEach(): 為數(shù)組中的每個(gè)元素執(zhí)行一次回調(diào)函數(shù),無(wú)返回值或默認(rèn)為undefined -
map(): 返回一個(gè)由回調(diào)函數(shù)的返回值組成的新數(shù)組 -
filter(): 將所有在過(guò)濾函數(shù)中返回true的數(shù)組元素放進(jìn)一個(gè)新數(shù)組中并返回 -
every(): 如果數(shù)組中的每個(gè)元素都滿(mǎn)足測(cè)試函數(shù),則返回true,否則返回false -
some(): 如果數(shù)組中至少由一個(gè)元素滿(mǎn)足測(cè)試函數(shù),則返回true,否則返回false
Q2. JS有哪幾種創(chuàng)建對(duì)象的方式
- 對(duì)象字面量
var obj = {} - Object 構(gòu)造函數(shù)
var obj = new Object - 工廠模式
缺點(diǎn): 每次通過(guò)function Person(name, age) { var o = new Object() o.name = name; o.age = age; o.say = function() { console.log(name) } return 0 }Person創(chuàng)建對(duì)象的時(shí)候,所有的say方法都是一樣的,但是卻存儲(chǔ)了多次,浪費(fèi)資源。 - 構(gòu)造函數(shù)模式
構(gòu)造函數(shù)模式隱式在最后返回function Person(name, age) { this.name = name this.age = age this.say = function() { console.log(name) } } var person = new Person('hello', 16)return this所以在缺少new的情況下,會(huì)將屬性和方法添加給全局對(duì)象,瀏覽器端就會(huì)添加給window對(duì)象,可以根據(jù)return this的特性調(diào)用call或者apply指定this - 原型模式
實(shí)現(xiàn)了方法和屬性的共享,可以動(dòng)態(tài)添加對(duì)象的屬性和方法。但是沒(méi)有辦法創(chuàng)建實(shí)例自己的屬性和方法,也沒(méi)有辦法傳遞參數(shù)。function Person() {} Person.prototype.name = 'Bob' Person.prototype.say = function() { alert(this.name); } Person.prototype.friends = ['Tom']; var person = new Person(); - 構(gòu)造函數(shù)和原型組合
function Person(name, age) { this.name = name this.age = age } Person.prototype.say = function() { console.log(this.name) } var person = new Person('hello')
Q3. JavaScript 如何實(shí)現(xiàn)繼承
-
原型鏈繼承
function Animal() {} Animal.prototype.name = 'cat' Animal.prototype.age = 2 Animal.prototype.say = function() { console.log('hello') } var cat = new Animal() cat.name // cat cat.age // 2 cat.say() // hello最簡(jiǎn)單的實(shí)現(xiàn)繼承的方式,但是也有缺點(diǎn)
1. 來(lái)自原型對(duì)象的所有屬性都被所有實(shí)例共享
2. 創(chuàng)建子類(lèi)實(shí)例時(shí),無(wú)法向父類(lèi)構(gòu)造函數(shù)傳參
3. 要想為子類(lèi)新增屬性和方法,必須要在new語(yǔ)句之后執(zhí)行,不能放到構(gòu)造器中 -
構(gòu)造繼承
function Animal() { this.species = '動(dòng)物' } function Cat(name, age) { Animal.call(this) this.name = name this.age = age } var cat = new Cat('coco', 2) cat.name // coco cat.age // 2 cat.species // 動(dòng)物使用
call或apply方法,將父對(duì)象的構(gòu)造函數(shù)綁定在子對(duì)象上 -
組合繼承
function Animal() { this.species = 'animal' } function Cat(name) { Animal.call(this) this.name = name } Cat.prototype = new Animal() // 重寫(xiě)原型 Cat.prototype.constructor = Cat如果沒(méi)有
Cat.prototype = new Animal()這一行,Cat.prototype.constructor是指向Cat的,加了這一行以后,Cat.prototype.constructor指向Animal. 這顯然會(huì)導(dǎo)致繼承鏈的紊亂(cat1明明是用構(gòu)造函數(shù)Cat生成的),因此必須手動(dòng)糾正,將Cat.prototype對(duì)象的constructor值改為Cat -
extends繼承ES6新增繼承方式,class可以通過(guò)extends關(guān)鍵字實(shí)現(xiàn)繼承class Animal {} class Cat extends Animal { constructor() { super(); } }使用
extends實(shí)現(xiàn)繼承,必須添加super關(guān)鍵字定義子類(lèi)的constructor,這里的super()就相當(dāng)于Animal.prototype.constructor.call(this)
Q4、同步和異步的區(qū)別,怎么異步加載JavaScript
同步模式
同步模式,又稱(chēng)為阻塞模式,JavaScript在默認(rèn)情況下是會(huì)阻塞加載的。當(dāng)前面的JavaScript請(qǐng)求沒(méi)有處理和執(zhí)行完時(shí),會(huì)阻止瀏覽器的后續(xù)處理
異步模式
異步加載又叫非阻塞,瀏覽器在下載執(zhí)行JavaScript同時(shí),還會(huì)繼續(xù)進(jìn)行后續(xù)頁(yè)面的處理
異步加載JavaScript
- 動(dòng)態(tài)添加
script標(biāo)簽 deferasync
defer屬性和async都是屬于script標(biāo)簽上面的屬性,兩者都能實(shí)現(xiàn)JavaScript的異步加載。不同之處在于:async在異步加載完成的時(shí)候就馬上開(kāi)始執(zhí)行了,而defer會(huì)等到html加載完畢之后再執(zhí)行
VUE篇
Q1. 寫(xiě) React / Vue 項(xiàng)目時(shí)為什么要在列表組件中寫(xiě) key,其作用是什么?
key是給每一個(gè)vnode的唯一id,可以依靠key,更準(zhǔn)確,更快的拿到oldVnode中對(duì)應(yīng)的vnode節(jié)點(diǎn)
1. 更準(zhǔn)確
帶key就不是就地復(fù)用了,在samenode函數(shù)a.key === b.key對(duì)比中可以避免就地復(fù)用的情況,所以更加準(zhǔn)確。
2. 更快
利用key的唯一性生成map對(duì)象來(lái)獲取對(duì)應(yīng)節(jié)點(diǎn),比遍歷方式更快。
vue和React都是采用diff算法來(lái)對(duì)比新舊虛擬節(jié)點(diǎn),從而更新節(jié)點(diǎn)。vue的diff算法在交叉對(duì)比中,當(dāng)新節(jié)點(diǎn)跟舊節(jié)點(diǎn)頭尾交叉對(duì)比沒(méi)有結(jié)果時(shí),會(huì)根據(jù)新節(jié)點(diǎn)的key去對(duì)比舊節(jié)點(diǎn)數(shù)組中的key,從而找到相應(yīng)舊節(jié)點(diǎn)(這里對(duì)應(yīng)的是一個(gè)key => index的map映射)。如果沒(méi)找到就認(rèn)為是一個(gè)新增節(jié)點(diǎn)。而如果沒(méi)有key,那么就會(huì)采用遍歷查找的方式去找到對(duì)應(yīng)的舊節(jié)點(diǎn)。一種是map映射,另一種是遍歷查找。相對(duì)比而言,map映射的速度更快。
vue 部分源碼如下:
// oldCh 是一個(gè)舊虛擬節(jié)點(diǎn)數(shù)組
if (isUndef(oldKeyToIdx)) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
if(isDef(newStartVnode.key)) {
// map 方式獲取
idxInOld = oldKeyToIdx[newStartVnode.key]
} esle {
// 遍歷方式獲取
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
}
創(chuàng)建map函數(shù)
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key
const map = {}
for(i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if(isDef(key)) map[key] = i
}
return map
}
遍歷尋找
// sameVnode 是對(duì)比新舊節(jié)點(diǎn)是否相同的函數(shù)
function findIdxInOld(node, oldCh, start, end) {
for(let i = start; i < end; i++) {
const c = oldCh[i]
if(isDef(c) && sameVnode(node, c)) return i
}
}