前言 由於個人的一些原因最近也參加了幾家公司的面試,發現有很多基礎性的東西掌握程度還是不夠,故此想總結一下最近面試遇到的問題,希望能為在準備面試的的小伙伴盡一些綿薄之力,主要說的是一些我面試當中問到的一些問題,說的不對的地方請小伙伴們即使指正出來,或者有其他的看法也可以一起探討。 一、HTML/CS ...
前言
由於個人的一些原因最近也參加了幾家公司的面試,發現有很多基礎性的東西掌握程度還是不夠,故此想總結一下最近面試遇到的問題,希望能為在準備面試的的小伙伴盡一些綿薄之力,主要說的是一些我面試當中問到的一些問題,說的不對的地方請小伙伴們即使指正出來,或者有其他的看法也可以一起探討。
一、HTML/CSS
1.html5新增標簽
- 新增了一些語義化的標簽例如:header,fotter,nav,main
- 新增圖像:canvas svg
- 新增媒體標簽: audio video
2.什麼是盒模型
- W3C標準盒模型,屬性width,height包含content,不包含border和padding
- IE盒模型,屬性width,height包含content,border,padding
3.css居中元素
說一下我比較常用的的幾種
- 使用position佈局
postion: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
- 使用flex佈局
display: flex; align-items: center; justify-content: center;
- 知道寬高的情況下使用position佈局
postion: absolute; left: 0; top: 0; right: 0; bottom: 0; margin: auto;
4.移動端1px邊框問題
主要是不同手機的dpi不同會導致這個問題。解決辦法:
- 看手機是否支持0.5px的邊框,並且dpi大於等於2,會用到js判斷比較複雜,這裡不做實現。
- 使用背景圖,修改顏色麻煩,需要換圖。圓角需要特殊處理。
- 偽類元素加transform實現,個人經常使用。
.border { position: relative; border:none; } .border:after { content: ''; position: absolute; bottom: 0; background: #000; width: 100%; height: 1px; transform: scaleY(0.5); }
5.css3有哪些新特性
- border新增border-img, border-raius
- 新增盒陰影box-shadow,文字陰影 text-shadow
- background新增background-image,background-size background-repeat
- 媒體查詢
6.rem em px區別
- px相對長度單位,相對於顯示器屏幕解析度來的
- em相對長度單位,相對於當前對象內文本的字體尺寸來的,值不固定,會繼承父級元素字體的大小,未經調整瀏覽器:16px = 1em。假如父元素為2em,子元素為2em字體實際大小為4em
- rem是css3新增的相對單位,使用rem為元素設置大小時,是相對大小,相對的是html根元素,修改根元素就可以調整所有字體大小,還可以避免字體大小逐層複合的連鎖反應,未經調整瀏覽器:16px = 1rem。
7.詳細說下BFC
- BFC塊級格式上下文,是頁面上一個獨立的容器,容器內的子元素不會影響到外邊的元素,垂直方向邊距重疊,計算高度是浮動元素也會計算
- BFC觸發:根元素(html),浮動元素(float不為none),絕對定位元素(position為absolute和fixed),行內塊元素(display為inline-block),overflow值不為visible,彈性元素(display為flex)
- 應用場景:設置元素為BFC防止浮動高度塌陷,避免外邊距重疊
8.重繪和迴流
- 簡單的一句話就是:迴流必引起重繪,重繪不會引起迴流,迴流比重繪更耗性能。
- 迴流:當元素的尺寸結構和某個屬性發生改變時,瀏覽器重新渲染部分或全部文檔的過程。
- 會發生迴流的操作:瀏覽器視窗發生改變,元素位置和大小發生改變,元素內容發生改變,對可見的dom進行添加或者刪除,查詢某些屬性或調用某些方法(clientWidth,offsetWidth, scrollWidth,scrollTo等等)
- 重繪:改變元素的樣式不影響在文檔流的位置(color,background-color)瀏覽器把新的樣式重新賦給元素並繪製
- css避免:避免設置多層內聯樣式,避免使用css表達式(ealc),將動畫放在position的屬性上(absolute, fixed),避免使用table佈局。
- js避免:避免重覆操作樣式(定義一個class並一次修改class屬性),避免頻繁操作dom(創建一個documentFragment,在它上邊操作dom,最後添加的文檔中),避免頻繁讀取引起重繪/迴流的值(可以使用變數緩存)。
二、JS
1.js基本類型和引用類型
- 基本類型:Boolean Null Number String Undefined Symbol BigInt
- 引用類型:Object
2.作用域
- 分為:全局作用域(定義在函數外部的變數)和局部作用域(定義在函數內部的變數),每個函數在被調用時都會創建一個新的域。ECMAScript 6引入了let和const關鍵字,利用let和const可以形成塊級作用域。
- 塊語句(if, switch, for, while)不會創建新的作用域,定義的變數保存在已經存在的作用域中,let和const支持在局部作用域塊語句中聲明
if (true) { var a = 1; let b = 2; const c =3; } console.log(a) // 1 console.log(b) // Uncaught ReferenceError: b is not defined console.log(c) // Uncaught ReferenceError: c is not defined
- 作用域鏈:作用域鏈是在變數對象之後創建的,作用域鏈用於解析變數,當變數被解析時,javascript先從代碼嵌套的最內層開始找,如果內層沒有找到,會轉到上一層父級作用域查找,直到找到該變數
3.閉包
- 它允許函數訪問局部作用域之外的數據,閉包有自己的作用域鏈,父級作用域鏈和全局作用域鏈。
function a() {
var b = 3;
return function() {
console.log(b)
}
}
a()() // 3
4.前端存儲cookie,localStorage,sessionStorage
- localStorage有效期為永久,sessionStorage有效期為視窗關閉
- 同源文檔可以修改並讀取localStorage的值,sessionStorage只允許同一個視窗下的文檔訪問
- 用法:
localStorage.setItem('a', 1); // storge a->1 localStorage.getItem('a'); // return value of a localStorage.removeItem('a'); // remove a localStorage.clear(); // remove all data
- cookie是瀏覽器儲存少量數據,cookie會自動在瀏覽器和伺服器之間傳輸,可以通過path和domain配置,頁面同目錄和子目錄都可以訪問
document.cookie = 'a=1; path=/'; // 創建當前頁面的cookie var a = document.cookie; // 讀取cookie 返回格式key=value; key1=value1; document.cookie = 'a=2; path=/'; // 修改值,會把以前的值覆蓋 document.cookie = "a=; expires=Thu, 01 Jan 1970 00:00:00 GMT"; //刪除cookie
5.promise實現原理
9k字 | Promise/async/Generator實現原理解析
可以看看蔡徐抻大佬總結的文章,非常詳細
6.巨集任務和微任務
- 首先你要知道Javascript是單線程語言,Event Loop是JavaScript的執行機制
- 微任務和巨集任務都屬於非同步任務,屬於同一個隊列,主要區別是他們執行的順序,開始執行巨集任務的時候,巨集任務執行完畢之後會看有沒有微任務,如果有的話執行微任務,沒有接著下一個執行巨集任務。在當前微任務沒有執行完畢,是不會執行下一個巨集任務的,而微任務又在巨集任務之前執行
console.log(1)
setTimeout(() => {
console.log(5)
},0)
new Promise(resolve => {
resolve()
console.log(2)
}).then(() => {
console.log(4)
})
console.log(3)
// 列印出1,2,3,4,5
- 巨集任務:setTimeout, setInterval, requestAnimationFrame
- 微任務 Promise.then catch finally
7.節流和防抖
- 節流:高頻事件觸發n秒內執行一次,如果這個時間點內觸發多次函數,只有一次生效。
function throttle(fn) {
var flag = true
return function() {
if (!flag) return;
flag = false;
setTimeout(function () {
fn()
flag = true
}, 1000)
}
}
- 防抖:高頻事件觸發n秒之後執行,如果n秒之內再次被觸發,重新記時。
function debounce(fn) {
var timeout = null;
return function() {
clearTimeout(timeout)
timeout = setTimeout(function (){
fn()
}, 1000)
}
}
8.get和post區別
- 最直觀的區別get把參數包含在URL中,post通過request body傳遞參數,相對於get比較安全
- get請求URL傳參有長度限制,post沒有
- get在瀏覽器回退是無害的,post會再次提交請求
- get請求會被瀏覽器主動緩存,post不會
- get和post報文格式不同
- get請求是冪等性的,而post請求不是(新增和刪除數據一般不用post請求就是這個原因)
9.Js的事件委托是什麼,原理是什麼
- 通俗點說將元素事件委托給他的父級或者更外級來處理
- 事件委托是利用冒泡機制來實現的(事件冒泡:事件由具體的元素接受,然後逐漸向上傳播到不具體的節點)
// 每個列表點擊彈出內容
// 不使用事件委托需要給每個列表添加點擊事件(消耗記憶體,不靈活,添加動態元素時需要重新綁定事件)這裡不做介紹
<ul id="myLink">
<li id="1">aaa</li>
<li id="2">bbb</li>
<li id="3">ccc</li>
</ul>
// 使用事件委托(減少記憶體占用,提升性能,動態添加元素無需重新綁定事件)
var myLink = document.getElementById('myLink');
myLink.onclick = function(e) {
var e = event || window.event;
var target = e.target || e.srcElement;
if(target.nodeName.toLowerCase() == 'li') {
alert(target.id + ':' + target.innerText);
}
};
10.什麼是原型鏈
- 原型:每個javascript創建的時候都會關聯另一個對象,這個對象就是原型,對象會從原型繼承屬性
- 構造函數可以通過prototype去尋找他關聯的原型,A.prototype就是它關聯的原型對象,原型對象可以通過構造器constructor來尋找與自身關聯的構造函數
function A () {
}
A.prototype.constructor === A //true
- 原型鏈:原型鏈是由原型對象組成,每個對象都有__proto__屬性,指向該構造函數的原型,__proto__將對象連接起來組成了原型鏈
- 原型鏈查找機制:當查找對象的屬性時,如果實例對象不存在該屬性,沿著原型鏈向上一級查找,直到找到object.prototype(也就是對象原型object.prototype為null),停止查找到返回undefined
function A () {
}
new A().__proto__ === A.prototype //true
- 原型上的屬性和方法被實例共用
function A () {
}
A.prototype.name = 'a'
var a = new A()
var b = new A()
a.name === b.name // true
a.__proto__.name === b.__proto__.name // true
- instanceOf原理:instamceOf可以判斷實例對象的__proto__屬性與構造函數的prototype是不是同一地址(如果網頁中有多個全局環境就會不准確)
function _instanceOf(obj, type) {
var obj = obj.__proto__
var type = type.prototype
while(true) {
if (obj == null) {
return false
}
if (obj == type) {
return true
}
obj = obj.__proto__
}
}
var a = [1, 2, 3]
_instanceOf(a, Array)
11.深拷貝和淺拷貝
- 淺拷貝只是複製引用,新舊對象共用一塊記憶體,一般把第一層拷貝到一個對象上,改變其中一個另一個也會改變
var obj = {
name: 'a',
age: 18,
arr: [1, 2]
}
function shallowCopy(obj) {
var newObj = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) { // 過濾掉原型上的屬性
newObj[prop] = obj[prop];
}
}
return newObj;
}
var obj1 = shallowCopy(obj)
var obj2 = obj
obj1.name = 'b'
obj2.age = 20
obj2.arr[0] = 3
obj1.arr[0] = 4
console.log(obj) // {name: "a", age: 20, arr: [4, 2]}
console.log(obj1) // {name: "b", age: 18, arr: [4, 2]}
console.log(obj2) // {name: "a", age: 20, arr: [4, 2]}
我們通過淺拷貝得到obj1,改變obj1的name,obj不會發生改變,通過賦值得到obj2,obj2改變age值obj的值也會被改變。說明賦值得到的對象只是改變了指針,淺拷貝是創建了新對象。
我們通過obj2和obj1都改變的值,發現obj,ob1,obj2都發生了改變,所以無論是淺拷貝還是賦值都會改變原始數據(淺拷貝只拷貝了一層對象的屬性)
- 深拷貝:複製並創建一個一摸一樣的對象(遞歸複製了所有層級),不共用記憶體,改變其中一個另一個不會改變
var obj = {
name: 'a',
age: 18,
arr: [1, 2]
}
function copy (obj) {
var newobj = Array.isArray(obj) ? [] : {};
if(typeof obj !== 'object'){
return;
}
for(var i in obj){
if (obj.hasOwnProperty(i)) {
newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
}
}
return newobj
}
var copyObj = copy(obj)
copyObj.arr[0] = 3
console.log(copyObj) //{name: "a", age: 20, arr: [3, 2]}
console.log(obj) //{name: "a", age: 20, arr: [1, 2]}
12.實現繼承
- 原型鏈繼承(在實例化一個類時,新創建的對象複製了父類構造函數的屬性和方法,並將__proto__指向父類的原型對象,當在子類上找不到對應的屬性和方法時,將會在父類實例上去找。)
function Big () {
this.age = [1, 2, 3]
}
Big.prototype.getAge = function () {
return this.age
}
function Small() {}
Small.prototype = new Big()
var small = new Small()
console.log(small.age) // [1, 2, 3]
console.log(small.getAge()) // [1, 2, 3]
缺點:無法為不同的實例初始化繼承來的屬性
function Big (name) {
this.age = [1, 2, 3]
this.name = name
}
Big.prototype.getAge = function () {
return this.age
}
function Small() {}
Small.prototype = new Big('small')
var small = new Small()
var small1 = new Small()
console.log(small1.name) // small
console.log(small.name) // small
- 構造函數繼承(在子類的構造函數中執行父類的構造函數,併為其綁定子類的this,讓父類的構造函數把成員屬性和方法都掛到子類的this上)
function Big (name) {
this.age = [1, 2, 3]
this.name = name
}
Big.prototype.getAge = function () {
return this.age
}
function Small(name) {
Big.apply(this, arguments)
}
var small = new Small('small')
var small1 = new Small('small1')
console.log(small.name) // small
console.log(small1.name) // small1
small.age[0] = 12
console.log(small.age) // [12, 2, 3]
console.log(small1.age) // [1, 2, 3]
缺點:無法訪問原型上的方法
small.getAge() //small.getAge is not a function
- 組合式繼承(將原型鏈繼承和構造函數繼承組合到一起, 綜合了原型鏈繼承和構造函數繼承的優點)
function Big (name) {
this.age = [1, 2, 3]
this.name = name
}
Big.prototype.getAge = function () {
return this.age
}
function Small(name) {
Big.apply(this, arguments)
}
Small.prototype = new Big()
var small = new Small('small')
var small1 = new Small('small1')
console.log(small.name) // small
console.log(small1.name) // small1
small.age[0] = 12
console.log(small.age) // [12, 2, 3]
console.log(small1.age) // [1, 2, 3]
console.log(small.getAge()) // [12, 2, 3]
console.log(small1.getAge()) // [1, 2, 3]
小缺點:調用了兩次父類構造函數
- 寄生組合式繼承(在組合繼承的基礎上減少一次多餘的調用父類構造函數)
function Big (name) {
this.age = [1, 2, 3]
this.name = name
}
Big.prototype.getAge = function () {
return this.age
}
function Small(name) {
Big.apply(this, arguments)
}
// 對父類原型進行拷貝,否則子類原型和父類原型指向同一個對象,修改子類原型會影響父類
Small.prototype = Object.create(Big.prototype)
var small = new Small('small')
var small1 = new Small('small1')
console.log(small.name) // small
console.log(small1.name) // small1
small.age[0] = 12
console.log(small.age) // [12, 2, 3]
console.log(small1.age) // [1, 2, 3]
console.log(small.getAge()) // [12, 2, 3]
console.log(small1.getAge()) // [1, 2, 3]
註意:對父類原型進行拷貝後賦值給子類原型,因此Small上的constructor屬性被重寫,需要修複Small.prototype.constructor = Dog;
- extends繼承(class和extends是es6新增的,class創建一個類,extends實現繼承)
class Big {
constructor(age) {
this.age = age;
}
getAge () {
return this.age
}
}
class Small extends Big {
constructor(age) {
super(age)
}
}
var small = new Small([1, 2, 3])
var small1 = new Small([12, 2, 3])
small.age[0] = 13
console.log(small.age) // [13, 2, 3]
console.log(small.getAge()) // [13, 2, 3]
console.log(small1.age) // [12, 2, 3]
console.log(small1.getAge()) // [12, 2, 3]
瀏覽器網路
1.常用http狀態碼
- 200 成功狀態碼
- 301 永久重定向,302 臨時重定向
- 400 請求語法錯誤, 401 請求需要http認證,403 不允許訪問資源,404 資源未找到
- 500 伺服器內部錯誤,502 訪問時出錯,503 伺服器忙,無法響應
2.https原理
- http協議:客戶端瀏覽器與web伺服器之間的應用層通訊協議
- https協議:HTTP+SSL/TLS,http下加入SSL層,https安全基礎時SSL,用於安全的HTTP數據傳輸
- https優勢:內容經過對稱加密,每個連接會生成唯一的加密密鑰,內容經過完整性校驗,第三方無法偽造身份
- 使用對稱加密(加密和解密使用的是同一個密鑰)被中間人攔截,中間人可以獲取密鑰,就可以對傳輸的信息進行窺視和篡改
- 使用非對稱密鑰(雙方必須協商一對密鑰,一個私鑰和一個公鑰)用私鑰加密的數據,只有對應的公鑰才能解密,用公鑰加密的數據,只有對應的私鑰才能解密,弊端:RSA演算法很慢
- 非對稱密鑰+對稱密鑰(結合兩者優點)
- 客戶端獲取公鑰確認伺服器身份通過SSL證書,客戶端接受到服務端發來的SSL證書給客戶端。
3.前端安全
- XSS攻擊:註入惡意代碼來攻擊。攻擊者在目標網站上註入惡意代碼,被攻擊者登陸網站執行這些惡意代碼,這些腳本可以讀取 cookie,session tokens,或者其它敏感的網站信息,對用戶進行釣魚欺詐(打開新標簽跳轉,新標簽存在惡意代碼,跳轉到偽造的頁面),網頁植入廣告等。
- XSS攻擊防禦手段:禁止JavaScript讀取某些敏感cookie,限制輸入內容和長度控制,檢測是否有惡意代碼註入
- CSRF攻擊:誘導用戶進入第三方,獲取到登錄憑證,冒充用戶對被攻擊的站點執行操作,導致賬號被劫持
- 防禦CSRF攻擊:驗證token(請求伺服器時,返回token,每個請求需要加上token)
5.瀏覽器緩存
- 瀏覽器每次發起請求,都會在瀏覽器緩存中查找請求結果和緩存標識,瀏覽器每次拿到的數據會將結果和標識存入瀏覽器中
- 強制緩存:當瀏覽器向伺服器發起請求時,伺服器會將緩存規則放入HTTP響應報文的HTTP頭中和請求結果一起返回給瀏覽器,控制強制緩存的欄位分別是Expires和Cache-Control,其中Cache-Control優先順序比Expires高。
- 協商緩存:強制緩存失效後,瀏覽器攜帶緩存標識向伺服器發起請求,由伺服器根據緩存標識決定是否使用緩存的過程。(協商緩存生效,伺服器返回304,資源未更新,協商緩存失效,伺服器返回200,資源更新重新緩存)
- 詳情可以看這篇文章徹底理解瀏覽器的緩存機制
框架
1.什麼是vue生命周期
- 每個vue實例在被創建之前都要經過一系列初始化過程,這個過程就是vue生命周期。(開始創建、初始化數據、編譯模板、掛載Dom→渲染、更新→渲染、卸載等一系列過程)
- beforeCreate: 完成實例初始化,this指向被創建的實例,data,computed,watch,mothods方法和數據都不可以訪問
- created: 實例創建完成,data,computed,watch,methods可被訪問,未掛載dom,可對data進行操作,操作dom需放到nextTick中
- beforeMount: 有了el,找到對應的template編譯成render函數
- mounted: 完成掛載dom和渲染,可以對dom進行操作,並獲取到dom節點,可以發起後端請求拿到數據
- beforeUpdate: 數據更新之前訪問現有dom,可以手動移除已添加事件的監聽
- updated: 組件dom已完成更新,可執行依賴的dom 操作,不要操作數據會陷入死迴圈
- activated: keep-alive緩存組件激活時調用
- deactivated keep-alive移除時調用
- deactivated keep-alive移除時調用
- beforeDestroy: 實例銷毀之前調用,可以銷毀定時器
- destroyed: 組件已經被銷毀
2.第一次頁面載入會觸發哪幾個鉤子
- 會觸發beforeCreate, created, beforeMount, mounted
3.created和mounted的區別
- created:在模板渲染成html前調用,即通常初始化某些屬性值,然後再渲染成視圖。
- mounted:在模板渲染成html後調用,通常是初始化頁面完成後,再對html的dom節點進行一些需要的操作。
4.hash模式和history模式
- 在vue的路由配置中有mode選項 最直觀的區別就是在url中 hash 帶了一個很醜的 # 而history是沒有#的。
- hash 雖然出現在 URL 中,但不會被包括在 HTTP 請求中,對後端完全沒有影響,因此改變 hash 不會重新載入頁面。
- history利用了HTML5中新增的 pushState() 和 replaceState() 方法。這兩個方法應用於瀏覽器的歷史記錄棧,在當前已有的 back、forward、go 的基礎之上,它們提供了對歷史記錄進行修改的功能。只是當它們執行修改時,雖然改變了當前的 URL,但瀏覽器不會立即向後端發送請求。
- history模式需要後臺配置支持
5.computed和watch區別
- computed: 依賴的屬性值發生改變才會重新計算,得出最後的值
- watch: 當依賴的data的數據變化,執行回調(可以觀察數據的某些變化,來做一些事情)
6.vue中key的作用
- key是給每一個vnode的唯一id,可以依靠key,更準確, 更快的拿到oldVnode中對應的vnode節點。
7.vue的兩個核心點
- 數據驅動:ViewModel,保證視圖的一致性
- 組件系統:組件化,封裝可復用的組件,頁面上每個獨立的區域都可以看成一個組件,組件可以自由組合成頁面
8.SPA首屏載入慢如何解決
- 使用路由懶載入
- 使用SSR渲染
- 優化webpack打包體積
- 圖片使用CDN加速
9.vue禁止彈窗後的屏幕滾動
- 主要是是寫一個點擊出現彈窗禁止屏幕滾動的方法,關閉彈窗屏幕可以正常滾動
methods : {
//禁止滾動
stop(){
var mo=function(e){e.preventDefault();};
document.body.style.overflow='hidden';
document.addEventListener("touchmove",mo,false);//禁止頁面滑動
},
/取消滑動限制/
move(){
var mo=function(e){e.preventDefault();};
document.body.style.overflow='';//出現滾動條
document.removeEventListener("touchmove",mo,false);
}
}
10.Vuex中actions和mutations的區別
- action主要處理的是非同步的操作,mutation必須同步執行,而action就不受這樣的限制,也就是說action中我們既可以處理同步,也可以處理非同步的操作
- action改變狀態,最後是通過提交mutation
結尾
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎點贊收藏關註三連,對作者也是一種鼓勵。一起在碼農的道路上越走越遠。