前端面試技巧

9月底秋招結束後,一直玩到今天。這篇博客在9月份就想寫來着,可那段時間太忙了,一天最多做過四個筆試,面試三家公司,確實沒有精力再寫博客了。這篇博客主要針對前端面試高頻問題,網上關於前端面試的題目有很多,但是很多題目過於陳舊,並不會被問到。以下內容是我親身經歷面試時經常被問到的點和如何回答的,希望給以後找工作的同學一點參考。在此也要感謝慕課網的《前端跳槽面試必備技巧》這門課程,如果有興趣的同學可以購買視頻課程學習。

頁面佈局

頁面佈局主要包括:三列布局、盒子水平垂直居中、BFC、浮動等內容。

三列布局

左中右三列布局實現的頁面效果:左右寬度已知,中間寬度自適應。 實現方法很多,至少說出以下幾種方法,除了實現以外,各種方法的優缺點也要知道。廢話不多說,直接上代碼。

  1. 浮動
<div class="con">
	<div class="left">left</div>
	<div class="right">right</div>
	<div class="center">center</div>
</div>
    .con div{
		height: 500px;
	}
	.left{
		float: left;
		width: 300px;
		background: yellow;
	}
	.right{
		float: right;
		width: 300px;
		background: red;
	}
	.center{
		background: blue;
	}

缺點:浮動會脫離文檔流
優點:兼容性好

  1. 絕對定位
<div class="con">
	<div class="left">left</div>
	<div class="right">right</div>
	<div class="center">center</div>
</div>
    .con div{
		height: 500px;
	}
	.left{
		position: absolute;
		left: 0;
		width: 300px;
		background-color: red;
	}
	.right{
		position: absolute;
		right: 0;
		width: 300px;
		background-color: red;
	}
	.center{//寬度不設置
		position: absolute;
		left: 300px;//關鍵
		right: 300px;
		background-color: yellow
	}

優點:快捷
缺點:脫離文檔流,可使用性較差

說到定位,很有可能問position屬性值又哪些?有什麼區別?
在這裏插入圖片描述
3. Flex佈局

<div class="con">
	<div class="left">left</div>
	<div class="center">center</div>
	<div class="right">right</div>
</div>
    .con{
		height: 100px;
		display: flex;
	}
	.left{
		width: 300px;
		background-color: red
	}
	.right{
		width: 300px;
		background-color: blue;
	}
	.center{
		flex:1;//中間自適應原理
		background-color: yellow
	}

缺點:兼容性

  1. table佈局
<div class="con">
	<div class="left">left</div>
	<div class="center">center</div>
	<div class="right">right</div>
</div>
    .con{
		height: 100px;
		width: 100%;
		display: table;
	}
	.left{
		width: 300px;
		display: table-cell;
		background-color: red
	}
	.right{
		width: 300px;
		display: table-cell;
		background-color: blue;
	}
	.center{
		display: table-cell;
		background-color: yellow
	}

盒子水平垂直居中

  1. 定位

父級相對定位,子級絕對定位,子級left\right\top\bottom設置爲0,margin:auto

<div class="con">
	<div class="center">center</div>	
</div>
.con{
	width: 400px;
	height: 400px;
	position: relative;
	background-color: blue;
}
.center{
    width: 200px;
    height: 200px;
    background: green;
    position:absolute;
    left:0;
    top: 0;
    bottom: 0;
    right: 0;
    margin: auto;
}
  1. margin 負間距法(必須知道子級元素的寬高)
<div class="con">
	<div class="center">center</div>	
</div>
.con{
	width: 400px;
	height: 400px;
	position: relative;
	background-color: blue;
}
.center{
    width:200px;
    height: 200px;
    background:red;
    position: absolute;
    left:50%;
    top:50%;
    margin-left:-100px;
    margin-top:-100px;
}
  1. CSS3的transform方法
<div class="con">
	<div class="center">center</div>	
</div>
.con{
	width: 400px;
	height: 400px;
	position: relative;
	background-color: blue;
}
.center{
    width: 200px;
    height: 200px;
    background: green;
    position:absolute;
    left:50%;    /* 定位父級的50% */
    top:50%;
    transform: translate(-50%,-50%); /*自己的50% */
}
  1. Flex佈局

父元素flex佈局,使用justify-content和align-items:center使子元素居中

<div class="con">
	<div class="center">center</div>	
</div>
.con{
	width: 400px;
	height: 400px;
	display:flex;
	justify-content:center;//水平方向
	align-items:center;//垂直方向
	background-color: red;
}
.center{
    width: 200px;
    height: 200px;
    background: green;
}

BFC

首先要知道什麼是BFC?

BFC:塊級格式化上下文

如何創建BFC?

(1)float屬性不爲none(浮動)
(2)position爲absolute或fixed(position絕對定位)
(3)display爲 table, inline-block, table-cell, table-caption, flex, inline-flex(display:table-cell)
(4)overflow不爲visible (overflow:hidden,overflow:auto)

BFC渲染規則?

(1)BFC垂直方向上的邊距會發生重疊
(2)BFC不會和浮動元素重疊(清除浮動)
(3)Bfc在頁面上是一個獨立的容器,外面的元素不會影響內部元素,內部的元素不會影響外部元素
(4)計算bfc高度的時候,浮動元素也會參與計算

BFC能夠做什麼?

(1)利用BFC避免外邊距摺疊,若兩個相鄰元素在不同的BFC中,就能避免外邊距摺疊。
(2)BFC包含浮動,解決容器高度不會被撐開的問題。同時也清除浮動!
(3)多欄佈局的一種方式。

浮動

爲什麼要浮動?

將多個塊級元素放到一行顯示

爲什麼要清除浮動?

清除浮動是爲了清除使用浮動帶來的影響,因爲浮動的元素被排除到文檔流之外,其餘元素會補位,造成高度塌陷,頁面佈局不能正常顯示。

清除浮動的方法?

  1. 在結尾處添加空div標籤clear:both
<div class="div1"> 
	<div class="left">Left</div> 
	<div class="right">Right</div>
	<div class="clearfloat"></div>
</div>
<div class="div2">div2</div>
   .div1{background:#000080;border:1px solid red}
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   
   /*清除浮動代碼*/
   .clearfloat{clear:both}

缺點:添加無意義的空標籤,不利於後期維護,在實際開發中不會用到。

  1. 父級div定義overflow:hidden
<div class="div1"> 
	<div class="left">Left</div> 
	<div class="right">Right</div>
</div>
<div class="div2">div2</div>
   .div1{background:#000080;border:1px solid red;/*解決代碼*/width:98%;overflow:hidden}
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px;width:98%}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}

必須定義width或zoom:1

  1. 父級元素也設置浮動

缺點:使得與父元素相鄰的元素佈局會受到影響,不可能一直浮動到body,只做瞭解,不推薦使用。

  1. 使用:after僞元素(實際開發中最常用的方法)

clearfix是父元素的一個類名。

   .clearfix:after{
		content:'';
		display:block;
		height:0;
		clear:both;
	}
	.clearfix{
		zoom:1/*爲了兼容ie6*/
	}

CSS盒模型

兩種盒子模型:標準盒子模型和IE盒子模型,在CSS3中新增了一個box-sizing屬性,這個屬性和盒子模型有關。box-sizing:content-box(默認)時,是標準盒子模型,box-sizing:border-box時,是IE盒子模型。

在這裏插入圖片描述

標準盒子模型中,width值content部分的寬度。

在這裏插入圖片描述

而在IE盒子模型中,width指的是content+paddingh+border這三部分的寬度。這就是兩種盒子模型的不同之處。

DOM事件

DOM事件的級別?

DOM0:element.onclick=function(){}   //後面的click事件會覆蓋前面的click事件
DOM2:element.addEventListener(‘click’,function(){},false)//false表示在冒泡階段調用事件處理程序,true表示在捕獲階段調用事件處理程序。允許給一個事件註冊多個監聽器,即後面的事件不會覆蓋前面的事件。
DOM3:element.addEventListener(‘keyup’,function(){},false)//增加了事件類型,例如鍵盤事件

事件流?

第一階段:捕獲,第二階段:目標階段,第三階段:冒泡

事件冒泡?

自下而上的觸發事件。

事件委託?

利用冒泡原理,目標元素將自身的響應事件委託給其父級元素來響應。

事件委託經典例子,經常問到:點擊li彈出,彈出li對應的索引值。
常見的做法:

<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
</ul>
var oli = document.getElementsByTagName('li');
for (let i = 0; i < oli.length; i++) {
	oli[i].onclick = function(){
		alert(i);
	}
};

注意:這裏的for循環內定義的變量 i 不能用 var 定義,因爲DOM事件是異步任務,因此此處如果使用var i = 0;那麼每次彈出的都是3,因爲當點擊事件觸發的時候,for循環已經執行完畢,i等於3。可以用let解決這個問題,因爲let是塊級作用域。

也可以使用立即執行函數來實現類似的塊級作用域的效果。

var oli = document.getElementsByTagName('li');
for (var i = 0; i < oli.length; i++) {
	(function(i){
		oli[i].onclick = function(){
			alert(i);
		}
	})(i)
};

最好的方法:使用事件委託實現:

var oul = document.querySelector('ul');
var oli = document.getElementsByTagName('li');

oul.addEventListener('click',function(event){
	var ev = event||window.event;
	var target = ev.target;
	for (var i = 0; i < oli.length; i++) {
		if(oli[i]== target){
			alert(i)
		}
	};
	/*if (target.nodeName === 'LI') { //彈出li裏面的內容
		alert(target.innerHTML)
	};*/
})

事件委託的好處:(1)不用循環遍歷所有的Li,然後爲Li添加事件。(2)減少對DOM的訪問而提高性能,監聽動態增加的元素(動態增加的元素不用重新綁定事件,事件委託給父級元素)

HTTP協議類

http協議的主要特點?

(1)簡單快速:想要訪問某個資源,只要輸入這個資源的uri(統一資源符)即可。
(2)靈活:每個http協議頭部分有一個數據類型,通過http協議可以完成不同類型的數據傳輸。
(3)無連接:服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。 (連接一次就會斷掉)
(4)無狀態:HTTP協議不對請求和響應之間的通信狀態進行保存。所以使用HTTP協議,每當有新的請求發送,就會有對應的新響應產生。

http報文?

(1)請求報文:輸入url地址,客戶端發送請求報文,包括:請求行(http方法,頁面地址,httpx協議及版本)、請求頭(key,value值,告訴服務端要哪些內容)、空行(分隔請求行和請求體)、請求體。
(2)響應報文:服務端接受到之後,做出響應,返回響應報文,包括:狀態行(http協議及版本,狀態碼)、響應頭、空行、響應體。

http方法?

Get---->獲取資源
Post---->傳輸資源
Head—>獲得報文首部
Put—>更新資源
Delete—>刪除資源

Post和get的區別:

(1)get在瀏覽器回退時是無害的(不會重複提交),而post會再次提交請求
(2)Get參數通過url傳遞,post請求會把提交的數據放在http請求報文的請求體中
(3)post比get安全,因爲get請求的參數直接暴露在url上,所以不能用來傳遞敏感信息
(4)Get請求的長度受 瀏覽器或服務器對url長度的 限制,允許發送的數據量較小,而Post沒有大小限制。

http狀態碼:

1開頭,指示信息
2開頭,響應成功
3開頭,服務器重定向響應
4開頭,客戶端錯誤
5開頭,服務端錯誤
常見http狀態碼:200,301,302,304,400,401,403,404,500,502,503,504
200 OK 正常返回信息。
301 Moved Permanently 永久重定向,請求的頁面已永久移動到新的位置。
302 Found 臨時性重定向。請求的頁面已經轉移至新的url。
304 Not Modified 自從上次請求後,請求的頁面未修改過,服務器告訴客戶端瀏覽器中有緩存,可以直接使用緩存。
400 Bad Request 客戶端有語法錯誤。
401 Unauthorized 請求未授權。
403 Forbidden 資源被禁止訪問。
404 Not Found 請求的資源不存在。
500 最常見的服務器端錯誤。
502 錯誤的網關。
503 服務器過載或維護。
504 網關超時。

原型鏈

創建對象有幾種方法?

//(1)對象字面量:
 var o1 ={
    Name:'xd'
}
//(2)
var o2 = new Object();
o2.name = 'xd';

//(3)
var p = {name:'xd'};
 var o3 = Object.create(P)//特點,03的原型對象就是p

原型對象,構造函數,實例之間的關係?

在這裏插入圖片描述

(1)每個構造函數都有一個prototype屬性,指向這個構造函數的原型對象。
(2)原型對象有一個constructor屬性,指向原型對象的構造函數。
(3)實例通過new一個構造函數創建,實例通過__proto__可以找到構造函數的原型對象。
(4)每個原型對象都有__proto__屬性,指向原型對象的上一級原型對象。

介紹一下原型鏈?

原型鏈:原型對象上的屬性和方法是被構造函數創建的實例所共有的,當訪問一個實例的屬性或方法的時候,在這個實例上本身上沒有找到這個屬性或方法,往原型對象上找,(通過__proto__),如果在上一級原型對象上還沒找到這個屬性或方法,會在這個原型對象的基礎上,沿着__proto__繼續往上一級原型對象查找,依次類推,直到找到一個名字匹配的屬性或方法或到達原型鏈的末尾:null。

擴展:介紹一下作用域鏈?

作用域:作用域的特點就是,先在自己的變量範圍中查找,如果找不到,就會沿着作用域往上找。

var a = 1;
function b(){ 
	var a = 2; 
	function c(){ 
		var a = 3;
		console.log(a); 
	} 
	c();
}
b();

最後打印出來的是3,因爲執行函數c()的時候它在自己的範圍內找到了變量a所以就不會越上繼續查找,如果在函數c()中沒有找到則會繼續向上找,一直會找到全局變量a,這個查找的過程就叫作用域鏈。

面向對象

類的聲明

兩種方法:(1)通過構造函數模擬一個類(2)ES6中對類的聲明

function Animal1(){//類+構造函數
	this.name = 'name'
}
class Animal2 {//類
	constructor(){//構造函數
		this.name
	}
}

JS 繼承

繼承實現的原理?

通過原型鏈實現繼承

有幾種方法實現繼承?各自優缺點?

  1. 借用構造函數實現繼承

原理:通過在子類的構造函數裏面執行父類的構造函數,父類構造函數裏的屬性和方法就都掛載到子類的實例上面了!

function Parent1(){
	this.name = "parent1"
}
Parent1.prototype.say = function(){}//原型對象上的方法並沒有辦法繼承的
function Child1(){
	Parent1.call(this);//在子類的構造函數裏面執行父級的構造函數
	this.type = 'child1';
}
console.log(new Child1,new Parent1())//沒有參數()可以省略

缺點:實現部分繼承!父類原型鏈上的屬性和方法並沒有被子類所繼承,子類繼承的只是父類構造函數裏面的屬性和方法。

  1. 原型鏈繼承

原理:將父類的實例賦值給子類的原型對象。

function Parent2(){
	this.name = 'parent2';
	this.play = [1,2,3]
}
function Child2(){
	this.type = 'child2'
}
Child2.prototype = new Parent2();//將父類的實例賦給子類的原型對象,這樣本來存在於父類實例中的所有屬性和方法,也都存在於子類的原型對象上了。

//存在的問題:
var s1 = new Child2();
var s2 = new Child2();
console.log(s1.play,s2.play)// [1, 2, 3] , [1, 2, 3]
s1.play.push(4);
console.log(s1.play,s2.play)// [1, 2, 3, 4] ,[1, 2, 3, 4]

缺點:問題主要在包含引用值的原型上,一個實例改變會導致原型上內容的改變。

  1. 組合繼承(僞經典繼承,實際開發中最常用的方法)

原理:是將原型鏈和借用構造函數組合到一起的一種繼承模式,主要思路就是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。

function Parent3(){
	this.name = 'parent3'
	this.play = [1,2,3]
}
function Child3(){
	Parent3.call(this);
	this.type='child3'
}
Child3.prototype = new Parent3();//此處是爲了解決“借用構造函數繼承”存在的缺點,即繼承父類原型對象上的屬性和方法。
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(3);
console.log(s3.play,s4.play)// [1, 2, 3, 3],[1, 2, 3]

缺點:可以看出在這個過程中父類的構造函數使用了兩次,一次在創建子類原型的時候,另一次是在子類構造函數內部,但是使用過程中,子類的實例屬性會屏蔽原型屬性,也就是說某些原型屬性其實是用不上的,這造成了內存的浪費。

  1. 組合模式的優化

借用構造函數存在缺點:實現部分繼承!父類原型鏈上的屬性和方法並沒有被子類所繼承,子類繼承的只是父類構造函數裏面的屬性和方法。
優化方法實現將構造函數的屬性和方法拿到,父類原型鏈上的屬性和方法也可以拿到,這樣父類只執行了一次。

function Parent4(){
	this.name = 'parent4'
	this.play = [1,2,3]
}
function Child4(){
	Parent4.call(this);
	this.type='child4'
}
Child4.prototype = Parent4.prototype;//子類和父類的原型對象是同一個(公用原型對象)
var s5 = new Child4();
var s6 = new Child4();
s5.play.push(3);
console.log(s5.play,s6.play)//(4) [1, 2, 3, 3] (3) [1, 2, 3]

缺點:無法判斷實例到底是哪個構造函數生成的

console.log(s5 instanceof Child4,s5 instanceof Parent4)//true true
  1. 經典繼承(最優方法,但是實際開發中用的並不多)

第一步常見父類類原型的一個副本。
第二步爲創建的副本添加constructer屬性,彌補一下因爲重寫原型而失去的默認的constructer屬性,
第三步將新創建的對象賦給子類的原型。

function Parent5(){
	this.name = 'parent5'
	this.play = [1,2,3]
}
function Child5(){
	Parent5.call(this);
	this.type='child5'
}
var obj1 = Object.create(Parent5.prototype);//Object.create創建的對象的特點:創建對象的原型對象就是括號裏的參數!
obj1.constructor = Child5;
Child5.prototype = obj1;
var s7 = new Child5();
console.log(s7)
console.log(s7 instanceof Child5,s7 instanceof Parent5)
console.log(s7.constructor === Child5)

注意:繼承這部分更詳細的介紹參考《JavaScript高級程序設計》(第三版),這本書裏有更加詳細的介紹,作爲面試考點,不會要求書裏寫的那幾種方法從頭到尾全部介紹一遍,可能會挑出一個最常用的方法,比如:組合繼承是怎麼實現的?最多再問一個如何經典繼承?原型鏈和繼承是JS裏面較難懂的部分,需要慢慢理解。

通信類

同源策略及限制

什麼是同源策略?

所謂“同源”指的是三個相同。

  • 協議相同
  • 域名相同
  • 端口相同

舉例來說,http://www.example.com/dir/page.html 這個網址,協議是http://,域名是www.example.com,端口是80(默認端口可以省略)。它的同源情況如下。
在這裏插入圖片描述
同源政策的目的?

保證用戶信息的安全,防止惡意的網站竊取數據。

限制範圍?

目前,如果非同源,共有三種行爲受到限制。
(1) Cookie、LocalStorage 和 IndexDB 無法讀取。
(2) DOM 無法獲得。
(3) AJAX 請求不能發送。(Ajax只適合同源的通信)

前後端通信的方式

常用方式:Ajax、WebSocket、CORS

什麼是Ajax?

異步的javascript和xml AJAX 是一種用於創建快速動態網頁的技術。 ajax用來與後臺交互。

WebSocket是一種通信協議,使用ws://(非加密)和wss://(加密)作爲協議前綴。該協議不實行同源政策,只要服務器支持,就可以通過它進行跨源通信。

CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。相比JSONP只能發GET請求,CORS允許任何類型的請求。

如何用原生JS創建Ajax?

  window.onload = function(){
        //第一步:創建XMLHttpRequest對象
        //xhr是一個對象;裏面可以放很多東西,數據;
        var xhr = null;
        if(window.XMLHttpRequest){//標準瀏覽器,判斷XMLHttpRequest對象是否存在
            xhr = new XMLHttpRequest();//創建一個對象
        }else{//早期的IE瀏覽器,兼容性
            xhr = new ActiveXObject('Microsoft.XMLHTTP');//參數是規定的;
        }
        console.log("狀態q"+xhr.readyState);//0
        //第二步:準備發送請求-配置發送請求的一些行爲,確定XMLHttpRequest對象的發送方式,是http的哪個協議,是get\post\delete等
        //open即打開鏈接,第一個參數是以什麼方式;第二個是往哪兒發送請求,第三個可以不寫,默認true,表示異步,false表示同步;;
        xhr.open('get','03form.php',true);
        console.log("狀態w"+xhr.readyState);//1

        //第三步:執行發送的動作
        //send也可以寫在前面,推薦寫在後面;寫null是兼容問題;
        xhr.send(null);
        console.log("狀態e"+xhr.readyState);//1

        //第四步:指定一些回調函數,也屬於事件函數;不觸發不執行,觸發條件是xhr.readyState;z這個值有0-4,共5個狀態,是由瀏覽器控制的;
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4){//4指服務器返回的數據可以使用;
                if(xhr.status == 200){ //判斷已經成功的獲取了數據;200表示hTTP請求成功;404表示找不到頁面;503表示服務器端有語法錯誤;
                    var data = xhr.responseText;//json,文本,主角;
                    // var data1 = xhr.responseXML;
                }
            }
            // console.log("狀態t"+xhr.readyState);//2表示已經發送完成;

            // console.log(1234);
        }

        // console.log(456);
        console.log("狀態r"+xhr.readyState);//1


    }

簡化版,說出關鍵步驟即可。

//步驟一:創建XMLHttpRequest對象
var ajax = new XMLHttpRequest();
//步驟二:用open確定XMLHttpRequest對象的發送方式put\get\post\delete
ajax.open('get','getStar.php?starName='+name);
//步驟三:發送請求
ajax.send();
//步驟四:註冊事件 onreadystatechange 狀態改變就會調用
ajax.onreadystatechange = function () {
   if (ajax.readyState==4 &&(ajax.status==200||ajax.status ==304)) {//readyState=4,說明請求完成。後面的ajax.status是判斷http的狀態碼,200一切正常,304表示重定向,利用本地緩存即可。
    //步驟五 如果能夠進到這個判斷 說明 數據 完美的回來了,並且請求的頁面是存在的
    console.log(ajax.responseText);//輸入相應的內容
    }
}

跨域

爲什麼要跨域?

同源策略的目標就是限制跨域通信,但實際業務中又需要跨域通信。

實現跨域的方法?

最少說出JSONP、CORS、postMessage,其他的方法例如hash、WebSocket瞭解即可。

  1. JSONP

原理:利用Script標籤的異步加載來實現。

JSONP是服務器與客戶端跨源通信的常用方法。最大特點就是簡單適用,老式瀏覽器全部支持,服務器改造非常小。它的基本思想是,網頁通過添加一個<script>元素,向服務器請求JSON數據,這種做法不受同源政策限制;服務器收到請求後,將數據放在一個指定名字的回調函數裏傳回來。

function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};

上面代碼通過動態添加<script>元素,向服務器example.com發出請求。注意,該請求的查詢字符串有一個callback參數,用來指定回調函數的名字,這對於JSONP是必需的。ip是向後臺請求的數據!
服務器收到這個請求以後,會將數據放在回調函數的參數位置返回。

foo({
  "ip": "8.8.8.8"
});

jQuery實現JSONP:指定’dataType:jsonp’即可,默認success作爲回調函數。

  1. CORS(Ajax的變種)

對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin字段。 簡單請求的請求方法:post,get,head.
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱爲"預檢"請求(preflight)。瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答覆,瀏覽器纔會發出正式的XMLHttpRequest請求,否則就報錯。

CORS與JSONP的使用目的相同,但是比JSONP更強大。
JSONP只支持GET請求,CORS支持所有類型的HTTP請求。JSONP的優勢在於支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。

  1. PostMessage(H5中新增的標準)

使用 postMessage 方法和 onmessage 事件來實現不同域之間的通信。
場景:當前頁面A通過iframe或frame嵌入了跨域的頁面B,A要給B發送消息。

舉例:在父框架頁面index.html發送obj對象給遠程服務器的 wozien.com/test/b.html ,該頁面是通過iframe加載的,如下:

/******************父窗口********************/
//父窗口給子窗口發消息, mes是要發送的信息
document.getElementByID('iframe').contentWindow.postMessage(msg,'子窗口源');
//document.getElementByID('iframe').contentWindow表示子窗口下的window對象

//監聽子窗口信息
window.addEventListener('message',function(event){ ... });
 
/******************子窗口********************/
//監聽父窗口信息 ,event.data接收父窗口傳來的數據(mes)
window.addEventListener('message',function(event){ console.log(event.data) })
//Onmessage事件,與onclick、onkeyup是一樣的!

//子窗口給父窗口發消息 
window.top.postMessage(msg,'父窗口源');

父窗口給子窗口發信息,需要用iframe的contentWindow屬性作爲調用主體。
子窗口給父窗口發的信息需要使用window.top。

瀏覽器渲染機制

瀏覽器渲染過程

  1. 解析HTML生成DOM樹。
  2. 解析CSS生成CSS規則樹。
  3. 將DOM樹與CSS規則樹合併在一起生成渲染樹。
  4. 佈局render樹,確定dom元素在頁面的位置大小等信息。
  5. 繪製render樹,渲染到頁面顯示。

重繪和重排

重排(迴流):當render tree中的一部分(或全部)因爲元素的規模尺寸,佈局,隱藏等改變而需要重新構建。這就稱爲迴流(reflow)。每個頁面至少需要一次迴流,就是在頁面第一次加載的時候。在迴流的時候,瀏覽器會使渲染樹中受到影響的部分失效,並重新構造這部分渲染樹,完成迴流後,瀏覽器會重新繪製受影響的部分到屏幕中,該過程成爲重繪。
重繪: 當render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風格,而不會影響佈局的,比如background-color。則就叫稱爲重繪。

注意:重排必將引起重繪,而重繪不一定會引起重排。
重排觸發條件?

(1)添加或者刪除可見的DOM元素;
(2)DOM元素位置改變;
(3)元素尺寸改變——邊距、填充、邊框、寬度和高度
(4)內容改變——比如文本改變或者圖片大小改變而引起的計算值寬度和高度改變;
(5)頁面渲染初始化;(每個頁面至少需要一次重排,就是在頁面第一次加載的時候。)
(6)瀏覽器窗口尺寸改變

重繪觸發條件?

改變元素外觀屬性。如:color,background-color等。

減少重繪的方法?

避免循環操作DOM。創建一個documentFragment或div,在它上面應用所有DOM操作,最後再把它添加到window.document。
1.將多次改變樣式的操作合併成一次。
2.將需要多次重排的元素,絕對定位(position屬性設爲absolute或fixed),這樣此元素就脫離了文檔流,它的變化不會影響到其他元素。

瀏覽器輸入URL後發生了什麼?

這是一道經典的面試題,由於此部分涉及的內容過多,這裏就不展開描述了,建議找一篇博客再細看一下。如果面試的時候實在想不起具體內容,最起碼以下八點要點還是要說出來的。

1.DNS域名解析;
2.建立TCP連接;
3.發送HTTP請求;
4.服務器處理請求;
5.返回響應結果;
6.關閉TCP連接;
7.瀏覽器解析HTML;
8.瀏覽器佈局渲染;

頁面性能

提升頁面性能的方法?
1、 資源壓縮合並,減少http請求。
2、非核心代碼異步加載->異步加載的方式->異步加載的區別
異步加載的方式(1)動態腳本加載(2)defer(3)async
動態腳本加載?

document.createElement(`'<script>'`);然後將創建的標籤加到body或head上:
document.body.appendChild()
document.getElementsByTagName("head")[0].appendChild()	

異步加載的區別?

(1)defer是在HTML解析之後纔會執行,如果是多個,按照加載的順序依次執行(2)async是在加載完之後立即執行,如果是多個,執行順序和加載順序無關!

3、利用瀏覽器緩存->緩存的分類->緩存的原理
瀏覽器緩存:資源文件在瀏覽器中存在的備份,或者成爲副本。例如網頁上的圖片請求回來,把圖片緩存到本地,存到電腦磁盤上,下次再訪問的時候,從磁盤直接讀,而不再請求圖片地址。
緩存分類?

(1)強緩存,在過期時間之前,不請求服務器,直接從瀏覽器副本拿過來用。http協議頭:在請求一個文件的時候,響應頭上回顯示expires(過期值)服務器的絕對時間,cache-contol:cache-control:max-age=3600客戶端相對時間。
(2)協商緩存,與協商緩存有關的http協議頭:
Last-modified(最後修改時間) 和if-modified-since,Etag和 if-none-match。

If-Modified-Since和 Last-Modified 一樣都是用於記錄頁面最後修改時間的 HTTP 頭信息,只是 Last-Modified 是由服務器往客戶端發送的 HTTP 頭,而 If-Modified-Since 則是由客戶端往服務器發送的頭,可 以看到,再次請求本地存在的 cache 頁面時,客戶端會通過 If-Modified-Since 頭將先前服務器端發過來的 Last-Modified 最後修改時間戳發送給服務器,這是爲了讓服務器端進行驗證,通過這個時間戳判斷客戶端的頁面是否是最新的,如果不是最新的,則返回新的內容,如果是最新的,則 返回 304 告訴客戶端其本地 緩存cache 的頁面是最新的,於是客戶端就可以直接從本地加載頁面了,這樣在網絡上傳輸的數據就會大大減少,同時也減輕了服務器的負擔。
瀏覽器發現本地有這個副本,但是瀏覽器不確定用不用這個備份,需要向服務器諮詢,要不要用副本。

ETags和If-None-Match的工作原理是在HTTP Response中添加ETags信息。當客戶端再次請求該資源時,將在HTTP Request中加入If-None-Match信息(ETags的值)。如果服務器驗證資源的ETags沒有改變(該資源沒有改變),將返回一個304狀態;否則,服務器將返回200狀態,並返回該資源和新的ETags。

4、使用CDN
網站上靜態資源即css、js全都使用cdn分發,圖片亦然,因爲cdn擁有衆多服務器,用戶請求可以請求距離他近的服務器,加快速度。

5、請減少對DOM的操作:創建一個documentFragment或div,在它上面應用所有DOM操作,最後再把它添加到window.document。

6、CSS放在頁面最上面,JavaScript放在頁面最下面
瀏覽器會在下載完全部CSS之後對整個頁面進行渲染,因此最好的做法是將CSS(link)放在<head></head>裏面,讓瀏覽器儘快下載CSS。JS則相反,JS文件過大有可能會阻塞整個頁面,造成頁面顯示緩慢,因此JS最好放在放在<body></body>之後。

安全類

前端安全涉及分類?

只要能答出一下兩種即可:CSRF和XSS。

CSRF:中文名稱:跨站請求僞造。

攻擊原理:
CSRF攻擊是源於WEB的隱式身份驗證機制,WEB的身份驗證機制雖然可以保證一個請求是來自於某個用戶的瀏覽器,但卻無法保證該請求是用戶批准發送的。跨站請求攻擊,簡單地說,是攻擊者通過一些技術手段欺騙用戶的瀏覽器去訪問一個自己曾經認證過的網站並以用戶的名義執行一些操作(如發郵件,發消息,甚至財產操作如轉賬和購買商品)。由於瀏覽器曾經認證過,所以被訪問的網站會認爲是真正的用戶操作而去執行。這利用了web中用戶身份驗證的一個漏洞:簡單的身份驗證只能保證請求發自某個用戶的瀏覽器,卻不能保證請求本身是用戶自願發出的。(兩個因素:接口存在漏洞,用戶在網站上登錄過)

防禦措施:加Token驗證、 HTTP Referer驗證、隱藏令牌
CSRF 攻擊之所以能夠成功,是因爲黑客可以完全僞造用戶的請求,該請求中所有的用戶驗證信息都是存在於 cookie 中,因此黑客可以在不知道這些驗證信息的情況下直接利用用戶自己的 cookie 來通過安全驗證。要抵禦 CSRF,關鍵在於在請求中放入黑客所不能僞造的信息,並且該信息不存在於 cookie 之中。可以在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 內容不正確,則認爲可能是 CSRF 攻擊而拒絕該請求。

XSS:中文名稱:跨站腳本攻擊。

攻擊原理:
xss跨站腳本攻擊(Cross Site Scripting),是一種經常出現在web應用中的安全漏洞,指攻擊者在網頁中嵌入客戶端腳本(例如JavaScript), 當用戶瀏覽此網頁時,腳本就會在用戶的瀏覽器上執行,從而達到攻擊者的目的。比如獲取用戶的Cookie,導航到惡意網站,攜帶木馬等。
大部分的xss漏洞都是由於沒有處理好用戶的輸入,導致攻擊腳本在瀏覽器中執行,這就是跨站腳本漏洞的根源。

XSS分類:反射型、存儲型和DOM XSS

(1)反射型
反射型惡意代碼並沒有保存在目標網站,大多數攻擊數據是包含在URL中,誘導用戶點擊鏈接,觸發 XSS 代碼,達到劫持訪問、獲取 cookies 的目的。
(2)存儲型(持久型)
通常是因爲服務器端將用戶輸入的惡意腳本沒有經過驗證就存儲在數據庫中,並且通過調用數據庫的方式,將數據呈現在瀏覽器上,每當用戶打開瀏覽器,惡意腳本就會執行。存儲型的 XSS 攻擊相比反射型的危害性更大,因爲每當用戶打開頁面,惡意腳本都會執行。
(3)DOM XSS
DOM XSS攻擊不同於反射型XSS和存儲型XSS,DOM XSS代碼不需要服務器端的解析,而是通過瀏覽器端的DOM解析。這完全是客戶端的事情。

XSS 漏洞修復

原則: 不相信客戶輸入的數據

(1)將重要的cookie標記爲http only, 這樣的話Javascript 中的document.cookie語句就不能獲取到cookie了.
(2)只允許用戶輸入我們期望的數據。 例如: 年齡的textbox中,只允許用戶輸入數字。 而數字之外的字符都過濾掉。
對數據進行Html Encode 處理
(3)過濾或移除特殊的Html標籤, 例如: <script>, <iframe> , < for <, > for >, &quot for
(4)過濾JavaScript 事件的標籤。例如 “onclick=”, “onfocus” 等等。
(5)DOM XSS的發生主要是在JS中使用eval造成的,所以應當避免使用eval語句。

XSS和CSRF區別?

CSRF攻擊的前提是用戶訪問一個已經認證過的網站,XSS是攻擊者在頁面內嵌攻擊腳本實現的。

結語

根據秋招面試經驗,以上問題經常被問到,在此特寫下這篇博客,希望給找工作的小夥伴一點參考。最後,祝好!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章