記錄和匯總看到的和碰到的前端面試題
主要來源:
https://github.com/Advanced-Frontend/Daily-Interview-Question
HTML + CSS 篇
Q1、 常用的行內(nèi)元素航背、塊級元素塞颁、空元素分別有哪些碳默?
1. 塊級元素:div ul ol li h1~h6 p table 等
2. 行內(nèi)元素: a span img input select 等
3. 空元素: br hr link等
Q2设哗、HTML語義化的理解
簡單來說诵棵,就是合適的標(biāo)簽做合適的事情
具體的好處有:
- 有助于構(gòu)建良好的HTML結(jié)構(gòu)疯潭,有利于搜索引擎的建立索引赊堪、抓取,利于SEO
- 有利于不同設(shè)備的解析
- 有利于團(tuán)隊(duì)的開發(fā)竖哩、維護(hù)
Q3哭廉、 常見的瀏覽器內(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é)取得頁面的內(nèi)容、整理訊息街立、計(jì)算網(wǎng)頁的顯示方式等舶衬;<br />
JS引擎則是解析JavaScript語言,執(zhí)行Javascript語言來實(shí)現(xiàn)網(wǎng)頁的動態(tài)效果赎离。
Q4逛犹、src 與 href 的區(qū)別
區(qū)別在于 src用于替代這個元素,而href用于建立這個標(biāo)簽與外部資源之間的關(guān)系梁剔。<br />
<link href="style.css" rel="stylesheet" />
瀏覽器加載到這里的時候虽画,html的渲染和解析不會暫停,css文件的加載是同時進(jìn)行的 <br />
<script src="script.js"></script>
當(dāng)瀏覽器解析到這句時荣病,頁面的加載和解析會暫停直到瀏覽器拿到和執(zhí)行完這個JS文件码撰。
Q5、CSS中l(wèi)ink 和 import 的區(qū)別
link屬于XHTML標(biāo)簽个盆,而@import完全是CSS提供的另一種方式脖岛,只能加載CSS<br />
加載順序不同:當(dāng)一個頁面被加載時,link引用的css會被同時加載颊亮,而import引用的css會等到頁面全部被下載完再被加載<br />
兼容性的區(qū)別:由于import時CSS2.1提出的所以老的瀏覽器不支持柴梆,而link標(biāo)簽無此問題<br />
當(dāng)使用JavaScript控制DOM去改變樣式時,只能用link標(biāo)簽终惑,因?yàn)锧import不是dom可以控制的
Q6绍在、CSS部分優(yōu)化、提高性能的方法有哪些雹有?
- 移除空的css規(guī)則
- 正確使用display屬性
- 不濫用浮動偿渡、web字體
- 不聲明過多的font-size
- 不在選擇器中使用ID標(biāo)識符
- 遵守盒模型規(guī)則
- 盡量減少頁面重排、重繪
- 提取公共樣式霸奕,減少代碼量
Q7溜宽、清除浮動的方式
浮動的元素是脫離文檔標(biāo)準(zhǔn)流的,如果不清除浮動质帅,就會造成父元素高度塌陷坑质,影響頁面布局
清除浮動的方式有:
- 為父元素設(shè)置高度
- 為父元素設(shè)置
overflow: hidden
- 偽元素
.father::after {
content: "";
display: block;
clear: both;
}
注:
- 使用偽元素的好處:不增加冗余的DOM節(jié)點(diǎn),符合語義化
-
overflow: hidden;
可以觸發(fā)BFC機(jī)制
Q8临梗、介紹BFC及其應(yīng)用
BFC就是塊級格式上下文涡扼,是頁面盒模型布局中的一種CSS渲染模式,創(chuàng)建了BFC的元素就相當(dāng)于一個獨(dú)立的盒子盟庞,它規(guī)定了內(nèi)部如何布局吃沪,里面的元素和外面的元素互不影響。 計(jì)算BFC的高度時什猖,浮動元素也參與計(jì)算票彪。
BFC的特性:
- 內(nèi)部box會在垂直方向上红淡,一個接一個的放置
- Box垂直方向的距離由margin決定,在一個BFC中降铸,兩個相鄰的塊級元素的垂直外邊距會產(chǎn)生重疊
- 在BFC中在旱,每個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對于從右到左的格式來說,則觸碰到右邊緣)
- 形成了BFC的區(qū)域不會與float box 重疊
- 計(jì)算BFC的高度時推掸,浮動元素也參與計(jì)算
創(chuàng)建BFC的方式有:
- html根元素
- float
- 絕對定位
- overflow 不為visiable
- display為表格布局或彈性布局
BFC主要的作用:
- 清除浮動
- 防止同一BFC容器中的相鄰元素之間的外邊距重疊的問題
Q9桶蝎、BFC、IFC谅畅、GFC 和 FFC
BFC( Block Formatting Contexts ):塊級格式上下文
頁面上的一個隔離的渲染區(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ì)算而來(不受到豎直方向的padding和margin影響), IFC中的line box 一般左右都貼緊整個IFC仇味,但是會因?yàn)閒loat元素而打亂呻顽。float元素會位于IFC 與 line box 之間,使得line box 寬度縮小丹墨。同時廊遍,IFC下的多個line box 高度會不同,IFC中是不可能有塊級元素的带到,當(dāng)插入塊級元素時昧碉,會產(chǎn)生兩個匿名塊與div隔開(即產(chǎn)生兩個IFC英染,每個IFC對外表現(xiàn)為塊級元素揽惹,與div垂直排列)。作用一般有:水平居中:當(dāng)一個塊要在環(huán)境中水平居中時四康,設(shè)置其為inline-block 則會在外層產(chǎn)生IFC搪搏,通過text-align則可以使其水平居中。垂直居中:創(chuàng)建一個IFC闪金,用其中一個元素?fù)伍_父元素的高度疯溺,然后設(shè)置其vertical-align: middle; 其他行內(nèi)元素就可以在此父元素下垂直居中。
GFC (GridLayout Formatting Contexts):網(wǎng)格化布局格式上下文
當(dāng)為一個元素設(shè)置display: grid; 的時候哎垦,此元素將會獲得一個獨(dú)立的渲染區(qū)域囱嫩,我們可以通過在網(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)為每一個網(wǎng)格項(xiàng)目定義位置和空間。與table同為二維的表格漏设,GridLayout會有更加豐富的屬性來控制行和列墨闲,控制對齊以及更為精細(xì)的渲染語義和控制。郑口、
FFC (Flex Formatting Contexts):自適應(yīng)格式上下文
diaplay為 flex 或 inline-flex 的元素將會生成自適應(yīng)容器(flex container)鸳碧,可惜這個屬性只有谷歌和火狐支持(移動端夠用了)盾鳞。Flex Box 由伸縮容器和伸縮項(xiàng)目組成。通過設(shè)置元素的display為flex 或 inline-flex 就可以得到一個伸縮容器瞻离。設(shè)置為flex的容器被渲染成一個塊級元素腾仅,而設(shè)置inline-box的元素被渲染成一個行內(nèi)元素。伸縮容器中的每一個子元素都是一個伸縮項(xiàng)目套利。伸縮項(xiàng)目可以是任意數(shù)量的推励。伸縮容器內(nèi)外元素互不影響。簡單的說就是日裙,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; 會讓元素完全從渲染樹中消失,渲染的時候不占據(jù)任何空間担钮,不能點(diǎn)擊;
visibility: hidden; 不會讓元素從渲染樹中消失格仲,渲染元素繼續(xù)占據(jù)空間吱晒,只是內(nèi)容看不見,不能點(diǎn)擊联四;
opacity: 0; 不會讓元素從渲染樹消失撑碴,渲染元素繼續(xù)占據(jù)空間,只是內(nèi)容看不見朝墩,可以點(diǎn)擊醉拓; - 繼承性
display: none;和 opacity: 0; 是非繼承屬性,子孫節(jié)點(diǎn)消失是由于元素從渲染樹消失造成的收苏,通過修改子孫節(jié)點(diǎn)的屬性無法將其顯示亿卤;
visibility: hidden; 是繼承屬性,子孫節(jié)點(diǎn)消失是由于繼承了hidden屬性鹿霸,通過給子孫節(jié)點(diǎn)設(shè)置visibility: visible;可以將子孫節(jié)點(diǎn)顯示出來排吴; - 性能
display: none; 修改元素會造成文檔回流,性能消耗較大懦鼠;
visibility: hidden; 修改元素只會造成重繪钻哩,性能消耗較少;
opacity: 0; 修改元素會造成重繪肛冶,性能消耗較少街氢; - 聯(liá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á)式將單詞與單個文字切割出來存入words
- 加上 '...'
- 判斷scrollHeight與clientHeight睦袖,超出的話就從words中pop一個出來
<p>測試文字珊肃,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ù)組對象有哪些常用的方法
修改器方法
-pop()
: 刪除數(shù)組的最后一個元素,并返回這個元素
-push()
: 在數(shù)組的末尾增加一個或多個元素近范,并返回數(shù)組的新長度
-reverse()
: 顛倒數(shù)組中元素的排列順序
-shift()
: 刪除數(shù)組的第一個元素嘶摊,并返回這個元素
-unshift()
: 在數(shù)組的開頭增加一個或多個元素,并返回數(shù)組的新長度
-sort()
: 對數(shù)組元素進(jìn)行排序评矩,并返回當(dāng)前數(shù)組
-splice()
: 在任意的位置給數(shù)組添加或刪除任意個元素
訪問方法
-concat()
: 返回一個由當(dāng)前數(shù)組和其它若干個數(shù)組或非數(shù)組值組合而成的新數(shù)組
-join()
: 連接所有數(shù)組元素組成一個字符串
-slice()
: 抽取當(dāng)前數(shù)組中的一段元素組合成一個新數(shù)組
-indexOf()
: 返回數(shù)組中第一個與指定值相等的元素的索引叶堆,如果找不到這樣的元素則返回-1
-lastIndexOf()
: 返回數(shù)組中最后一個(從右邊數(shù)第一個)與指定值相等的元素的索引,如果找不到這樣的元素則返回-1
迭代方法
-
forEach()
: 為數(shù)組中的每個元素執(zhí)行一次回調(diào)函數(shù)斥杜,無返回值或默認(rèn)為undefined
-
map()
: 返回一個由回調(diào)函數(shù)的返回值組成的新數(shù)組 -
filter()
: 將所有在過濾函數(shù)中返回true
的數(shù)組元素放進(jìn)一個新數(shù)組中并返回 -
every()
: 如果數(shù)組中的每個元素都滿足測試函數(shù)虱颗,則返回true
,否則返回false
-
some()
: 如果數(shù)組中至少由一個元素滿足測試函數(shù)蔗喂,則返回true
忘渔,否則返回false
Q2. JS有哪幾種創(chuàng)建對象的方式
- 對象字面量
var obj = {}
- Object 構(gòu)造函數(shù)
var obj = new Object
- 工廠模式
缺點(diǎn): 每次通過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)建對象的時候,所有的say
方法都是一樣的缰儿,但是卻存儲了多次畦粮,浪費(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
的情況下乖阵,會將屬性和方法添加給全局對象宣赔,瀏覽器端就會添加給window
對象,可以根據(jù)return this
的特性調(diào)用call
或者apply
指定this
- 原型模式
實(shí)現(xiàn)了方法和屬性的共享瞪浸,可以動態(tài)添加對象的屬性和方法儒将。但是沒有辦法創(chuàng)建實(shí)例自己的屬性和方法,也沒有辦法傳遞參數(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
最簡單的實(shí)現(xiàn)繼承的方式钩蚊,但是也有缺點(diǎn)
1. 來自原型對象的所有屬性都被所有實(shí)例共享
2. 創(chuàng)建子類實(shí)例時,無法向父類構(gòu)造函數(shù)傳參
3. 要想為子類新增屬性和方法蹈矮,必須要在new
語句之后執(zhí)行砰逻,不能放到構(gòu)造器中 -
構(gòu)造繼承
function Animal() { this.species = '動物' } 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 // 動物
使用
call
或apply
方法,將父對象的構(gòu)造函數(shù)綁定在子對象上 -
組合繼承
function Animal() { this.species = 'animal' } function Cat(name) { Animal.call(this) this.name = name } Cat.prototype = new Animal() // 重寫原型 Cat.prototype.constructor = Cat
如果沒有
Cat.prototype = new Animal()
這一行含滴,Cat.prototype.constructor
是指向Cat
的诱渤,加了這一行以后丐巫,Cat.prototype.constructor
指向Animal
. 這顯然會導(dǎo)致繼承鏈的紊亂(cat1
明明是用構(gòu)造函數(shù)Cat
生成的)谈况,因此必須手動糾正,將Cat.prototype
對象的constructor
值改為Cat
-
extends
繼承ES6
新增繼承方式递胧,class
可以通過extends
關(guān)鍵字實(shí)現(xiàn)繼承class Animal {} class Cat extends Animal { constructor() { super(); } }
使用
extends
實(shí)現(xiàn)繼承碑韵,必須添加super
關(guān)鍵字定義子類的constructor
,這里的super()
就相當(dāng)于Animal.prototype.constructor.call(this)
Q4缎脾、同步和異步的區(qū)別祝闻,怎么異步加載JavaScript
同步模式
同步模式,又稱為阻塞模式,JavaScript
在默認(rèn)情況下是會阻塞加載的联喘。當(dāng)前面的JavaScript
請求沒有處理和執(zhí)行完時华蜒,會阻止瀏覽器的后續(xù)處理
異步模式
異步加載又叫非阻塞,瀏覽器在下載執(zhí)行JavaScript
同時豁遭,還會繼續(xù)進(jìn)行后續(xù)頁面的處理
異步加載JavaScript
- 動態(tài)添加
script
標(biāo)簽 defer
async
defer
屬性和async
都是屬于script
標(biāo)簽上面的屬性叭喜,兩者都能實(shí)現(xiàn)JavaScript
的異步加載。不同之處在于:async
在異步加載完成的時候就馬上開始執(zhí)行了蓖谢,而defer
會等到html
加載完畢之后再執(zhí)行
VUE篇
Q1. 寫 React / Vue 項(xiàng)目時為什么要在列表組件中寫 key捂蕴,其作用是什么?
key是給每一個vnode的唯一id闪幽,可以依靠key啥辨,更準(zhǔn)確,更快的拿到oldVnode中對應(yīng)的vnode節(jié)點(diǎn)
1. 更準(zhǔn)確
帶key就不是就地復(fù)用了盯腌,在samenode函數(shù)a.key === b.key
對比中可以避免就地復(fù)用的情況溉知,所以更加準(zhǔn)確。
2. 更快
利用key的唯一性生成map對象來獲取對應(yīng)節(jié)點(diǎn)腕够,比遍歷方式更快着倾。
vue和React都是采用diff算法來對比新舊虛擬節(jié)點(diǎn),從而更新節(jié)點(diǎn)燕少。vue的diff算法在交叉對比中卡者,當(dāng)新節(jié)點(diǎn)跟舊節(jié)點(diǎn)頭尾交叉對比沒有結(jié)果時,會根據(jù)新節(jié)點(diǎn)的key去對比舊節(jié)點(diǎn)數(shù)組中的key客们,從而找到相應(yīng)舊節(jié)點(diǎn)(這里對應(yīng)的是一個key => index的map映射)崇决。如果沒找到就認(rèn)為是一個新增節(jié)點(diǎn)。而如果沒有key底挫,那么就會采用遍歷查找的方式去找到對應(yīng)的舊節(jié)點(diǎn)恒傻。一種是map映射,另一種是遍歷查找建邓。相對比而言盈厘,map映射的速度更快。
vue 部分源碼如下:
// oldCh 是一個舊虛擬節(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 是對比新舊節(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
}
}