一、 原型和原型鏈
- 所有的引用類型(數(shù)組、函數(shù)、對象)可以自由擴(kuò)展屬性(除null以外)。
- 引用類型有一個(gè)_ _ proto_ _屬性(也叫隱式原型,它是一個(gè)普通的對象)。
- 函數(shù)有一個(gè)prototype屬性(這也叫顯式原型,它也是一個(gè)普通的對象)。
- 引用類型的_ _ proto_ _指向它構(gòu)造函數(shù)的prototype。
- 當(dāng)試圖得到一個(gè)對象的屬性時(shí),如果這個(gè)對象本身不存在這個(gè)屬性,那么就會(huì)去它的_ _ proto_ _屬性(也就是它的構(gòu)造函數(shù)的’prototype’屬性)中去尋找。
function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 順便學(xué)習(xí)一個(gè)ES5的方法,可以獲得對象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
-
原型鏈:利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。然后層層遞進(jìn),就構(gòu)成了實(shí)例與原型的鏈條。
image.png new操作符具體干了什么呢?
1、創(chuàng)建一個(gè)空對象,并且 this 變量引用該對象,同時(shí)還繼承了該函數(shù)的原型。
2、屬性和方法被加入到 this 引用的對象中。
3、新創(chuàng)建的對象由 this 所引用,并且最后隱式的返回 this 。
閉包
閉包是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
1.優(yōu)點(diǎn):
- 變量長期駐扎在內(nèi)存中;
- 避免全局變量的污染;
- 私有成員的存在 ;
2. 特性 - 函數(shù)套函數(shù);
- 內(nèi)部函數(shù)可以直接使用外部函數(shù)的局部變量或參數(shù);
- 變量或參數(shù)不會(huì)被垃圾回收機(jī)制回收 GC;
3.缺點(diǎn)
常駐內(nèi)存 會(huì)增大內(nèi)存的使用量 使用不當(dāng)會(huì)造成內(nèi)存泄露,詳解: -
內(nèi)存泄漏:每個(gè)瀏覽器會(huì)有自己的一套回收機(jī)制,當(dāng)分配出去的內(nèi)存不使用的時(shí)候便會(huì)回收;內(nèi)存泄露的根本原因就是你的代碼中分配了一些‘頑固的’內(nèi)存,瀏覽器無法進(jìn)行回收,如果這些’頑固的’內(nèi)存還在一直不停地分配就會(huì)導(dǎo)致后面所用內(nèi)存不足,造成泄露。 - 由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁的性能問題,導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
// setTimeout保存循環(huán)中的值
for(var i=0; i< 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i));
}
i = null;
堆和棧
堆和棧在不同的場景下,有不同的含義:
(1)程序內(nèi)存布局場景下,堆與棧表示兩種內(nèi)存管理方式;
(2)數(shù)據(jù)結(jié)構(gòu)場景下,堆與棧表示兩種常用的數(shù)據(jù)結(jié)構(gòu)。
- 這里說的是內(nèi)存中的堆和棧
變量都存放在內(nèi)存中
內(nèi)存給變量開辟了兩塊區(qū)域,分別為棧區(qū)域和堆區(qū)域
棧的特點(diǎn),開口向上,速度快,容量小
堆的特點(diǎn),速度稍慢,容量比較大
-
棧(stack)
棧區(qū)存放基本類型和引用類型的指針
image.png -
堆(heap)
堆區(qū)存放引用類型,比如:Array,Object對象
image.png
補(bǔ)充:
基本類型: undefined,boolean,number,string,null.
引用類型:object、Array、RegExp、Date、Function、特殊的基本包裝類型(String、Number、Boolean)以及單體內(nèi)置對象(Global、Math)。
數(shù)據(jù)查詢速度比較,stack遠(yuǎn)遠(yuǎn)大于heap。
在實(shí)際開發(fā)過程中,偶爾遇到棧溢出的情況,stack overflow錯(cuò)誤,因?yàn)閟tack創(chuàng)建時(shí)候,大小是確定的,超過額度大小就會(huì)發(fā)生棧溢出【當(dāng)js出現(xiàn)死循環(huán)或者錯(cuò)誤的遞歸時(shí)候】。heap大小是不確定的,需要可以一直累加。
js是單線程的,那么怎么利用多核的CPU呢?H5的Web Worker標(biāo)準(zhǔn),允許js腳本創(chuàng)建多個(gè)線程,但是子線程受主線程的控制,且不能操作DOM。
stack是線程獨(dú)占的,heap是線程共有的。
- 基本包裝類型
String Number Boolean這三個(gè)基本類型有其對應(yīng)的包裝對象,包裝對象有其對應(yīng)的屬性和方法,調(diào)用方法的過程是在后臺(tái)發(fā)生的
var str = 'hello'; //string 基本類型
var s2 = str.charAt(0); //在執(zhí)行到這一句的時(shí)候 后臺(tái)會(huì)自動(dòng)完成以下動(dòng)作 :
(
var _str = new String('hello'); // 1 找到對應(yīng)的包裝對象類型,然后通過包裝對象創(chuàng)建出一個(gè)和基本類型值相同的對象
var s2 = _str.chaAt(0); // 2 然后這個(gè)對象就可以調(diào)用包裝對象下的方法,并且返回結(jié)給s2.
_str = null; // 3 之后這個(gè)臨時(shí)創(chuàng)建的對象就被銷毀了, str =null;
)
alert(s2);//h
alert(str);//hello
注意這一瞬間,我們并沒有改變原字符串str的值,只是新建了一個(gè)_str對象得出結(jié)果,賦值給s2
引用類型創(chuàng)建的對象在執(zhí)行期間一直存在,包裝類型創(chuàng)建的對象只存在了一瞬間
- 淺拷貝和深拷貝
- 淺拷貝,拷貝了對象的引用,原對象變化時(shí),拷貝對象變化
- 深拷貝,拷貝了對象的值,原對象變化時(shí),拷貝對象不變
深拷貝的方法 -
obj2 = Object.assign({}, obj1)
創(chuàng)建了一個(gè)空對象,把obj1屬性復(fù)制過去,obj1改變不會(huì)影響obj2 -
obj2 = JSON.parse(JSON.stringify(obj1))
JSON.stringify把對象轉(zhuǎn)成字符串,JSON.parser把字符串轉(zhuǎn)成新對象 - 遞歸拷貝
瀏覽器渲染原理
1.瀏覽器渲染過程
- HTML文檔解析成DOM樹 。
- 處理CSS標(biāo)記,構(gòu)成層疊樣式表模型CSSOM(CSS Object Model)
- 將DOM和CSSOM合并為渲染樹(rendering tree)。
在這一過程中,不是簡單的將兩者合并就行了。渲染樹只會(huì)包括需要顯示的節(jié)點(diǎn)和這些節(jié)點(diǎn)的樣式信息,如果某個(gè)節(jié)點(diǎn)是 display: none 的,那么就不會(huì)在渲染樹中顯示。 -
當(dāng)瀏覽器生成渲染樹之后,就會(huì)根據(jù)渲染樹來進(jìn)行布局。這一個(gè)階段瀏覽器要做的事情就是要弄清楚每個(gè)節(jié)點(diǎn)在頁面中的確切位置和大小。通常這一個(gè)行為也叫做自動(dòng)重排。
布局流程的輸出是一個(gè)盒模型,它會(huì)精確的捕獲每個(gè)元素在視口內(nèi)的確切位置和尺寸,所有相對測量值都將轉(zhuǎn)換成屏幕上的絕對像素。
image.png
2.渲染阻塞
構(gòu)建DOM樹和CSSOM樹時(shí),遇到JS,整個(gè)解析進(jìn)程必須等待JS的執(zhí)行完成才能夠繼續(xù),這就是所謂的JS阻塞頁面。
每次去執(zhí)行JavaScript腳本都會(huì)嚴(yán)重地阻塞DOM樹的構(gòu)建,如果JavaScript腳本還操作了CSSOM,而正好這個(gè)CSSOM還沒有下載和構(gòu)建,瀏覽器甚至?xí)舆t腳本執(zhí)行和構(gòu)建DOM,直至完成其CSSOM的下載和構(gòu)建。所以,script標(biāo)簽的位置很重要。
由于CSSOM負(fù)責(zé)存儲(chǔ)渲染信息,瀏覽器就必須保證在合成渲染樹之前,CSSOM和DOM的解析完全結(jié)束,瀏覽器才會(huì)進(jìn)入下一步的渲染,這就是CSS阻塞渲染。
CSS阻塞渲染意味著,在CSSOM完備前,頁面將一直處理白屏狀態(tài),這就是為什么樣式放在head中,僅僅是為了更快的解析CSS,保證更快的首次渲染。
需要注意的是,即便你沒有給頁面任何的樣式聲明,CSSOM依然會(huì)生成,默認(rèn)生成的CSSOM自帶瀏覽器默認(rèn)樣式。
3. 回流(重排)和重繪(reflow和repaint)
HTML默認(rèn)是流式布局的,但CSS和JS會(huì)打破這種布局,改變DOM的外觀樣式以及大小和位置。因此我們就需要知道兩個(gè)概念:replaint和reflow。
3.1 reflow(回流/重排)
當(dāng)瀏覽器發(fā)現(xiàn)布局發(fā)生了變化,這個(gè)時(shí)候就需要倒回去重新渲染,大家稱這個(gè)回退的過程叫reflow。
reflow會(huì)從html這個(gè)root frame開始遞歸往下,依次計(jì)算所有的結(jié)點(diǎn)幾何尺寸和位置,以確認(rèn)是渲染樹的哪一部分發(fā)生變化還是整個(gè)渲染樹。reflow幾乎是無法避免的,因?yàn)橹灰脩暨M(jìn)行交互操作,就勢必會(huì)發(fā)生頁面的一部分的重新渲染,且通常我們也無法預(yù)估瀏覽器到底會(huì)reflow哪一部分的代碼,因?yàn)樗麄儠?huì)相互影響。
3.2 repaint(重繪)
repaint則是當(dāng)我們改變某個(gè)元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性時(shí),屏幕的一部分要重畫,但是元素的幾何尺寸和位置沒有發(fā)生改變。
注意,display:none會(huì)觸發(fā)reflow,visibility: hidden只會(huì)觸發(fā)repaint
visibiliy是隱藏元素,但元素仍然占據(jù)著布局空間,它會(huì)被渲染成一個(gè)空框。所以visibility:hidden只會(huì)觸發(fā)repaint,因?yàn)闆]有發(fā)生位置變化。
另外,修改了元素的樣式,瀏覽器并不會(huì)立刻reflow或repaint一次,而是會(huì)把這樣的操作積攢一批,然后做一次reflow,這又叫異步reflow或增量異步reflow。
但是在有些情況下,比如resize窗口,改變了頁面默認(rèn)的字體等。對于這些操作,瀏覽器會(huì)馬上進(jìn)行reflow。
3.3 引起reflow
現(xiàn)代瀏覽器會(huì)對回流做優(yōu)化,它會(huì)等到足夠數(shù)量的變化發(fā)生,再做一次批處理回流。
- 頁面第一次渲染(初始化)
- DOM樹變化(如:增刪節(jié)點(diǎn))
- Render樹變化(如:padding改變)
- 瀏覽器窗口resize
- 獲取元素的某些屬性
瀏覽器為了獲得正確的值也會(huì)提前觸發(fā)回流,這樣就使得瀏覽器的優(yōu)化失效了,這些屬性包括offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、調(diào)用了getComputedStyle()。
3.4 引起repaint
reflow回流必定引起repaint重繪,重繪可以單獨(dú)觸發(fā)。
背景色、顏色、字體改變(注意:字體大小發(fā)生變化時(shí),會(huì)觸發(fā)回流)
3.5 減少reflow、repaint觸發(fā)次數(shù)
- 用transform做形變和位移可以減少reflow
- 避免逐個(gè)修改節(jié)點(diǎn)樣式,盡量一次性修改
- 使用DocumentFragment將需要多次修改的DOM元素緩存,最后一次性append到真實(shí)DOM中渲染
- 可以將需要多次修改的DOM元素設(shè)置display:none,操作完再顯示。(因?yàn)殡[藏元素不在render樹內(nèi),因此修改隱藏元素不會(huì)觸發(fā)回流重繪)
- 避免多次讀取某些屬性
- 通過絕對位移將復(fù)雜的節(jié)點(diǎn)元素脫離文檔流,形成新的Render Layer,降低回流成本
4. 幾條關(guān)于優(yōu)化渲染效率的建議
- 合法地去書寫HTML和CSS ,且不要忘了文檔編碼類型。
- 樣式文件應(yīng)當(dāng)在head標(biāo)簽中,而腳本文件在body結(jié)束前,這樣可以防止阻塞的方式。
- 簡化并優(yōu)化CSS選擇器,盡量將嵌套層減少到最小。
- DOM 的多個(gè)讀操作(或多個(gè)寫操作),應(yīng)該放在一起。不要兩個(gè)讀操作之間,加入一個(gè)寫操作。
- 如果某個(gè)樣式是通過重排得到的,那么最好緩存結(jié)果。避免下一次用到的時(shí)候,瀏覽器又要重排
- 不要一條條地改變樣式,而要通過改變class,或者csstext屬性,一次性地改變樣式。
- 盡量用transform來做形變和位移
- 盡量使用離線DOM,而不是真實(shí)的網(wǎng)面DOM,來改變元素樣式。比如,操作Document Fragment對象,完成后再把這個(gè)對象加入DOM。再比如,使用cloneNode()方法,在克隆的節(jié)點(diǎn)上進(jìn)行操作,然后再用克隆的節(jié)點(diǎn)替換原始節(jié)點(diǎn)。
- 先將元素設(shè)為display: none(需要1次重排和重繪),然后對這個(gè)節(jié)點(diǎn)進(jìn)行100次操作,最后再恢復(fù)顯示(需要1次重排和重繪)。這樣一來,你就用兩次重新渲染,取代了可能高達(dá)100次的重新渲染。
- position屬性為absolute或fixed的元素,重排的開銷會(huì)比較小,因?yàn)椴挥每紤]它對其他元素的影響
- 只在必要的時(shí)候,才將元素的display屬性為可見,因?yàn)椴豢梢姷脑夭挥绊懼嘏藕椭乩L。另外,visibility : hidden的元素只對重繪有影響,不影響重排。
- 使用window.requestAnimationFrame()、window.requestIdleCallback()這兩個(gè)方法調(diào)節(jié)重新渲染。
http狀態(tài)碼
| 分類 | 分類描述 |
|---|---|
| 1** | 信息,服務(wù)器收到請求,需要請求者繼續(xù)執(zhí)行操作 |
| 2** | 成功,操作被成功接收并處理 |
| 3** | 重定向,需要進(jìn)一步的操作以完成請求 |
| 4** | 客戶端錯(cuò)誤,請求包含語法錯(cuò)誤或無法完成請求 |
| 5** | 服務(wù)器錯(cuò)誤,服務(wù)器在處理請求的過程中發(fā)生了錯(cuò)誤 |
| 狀態(tài)碼 | 狀態(tài)碼英文名稱 | 中文描述 |
|---|---|---|
| 200 | OK | 請求成功。 |
| 301 | Moved Permanently | 永久移動(dòng)。資源(網(wǎng)頁等)被永久轉(zhuǎn)移到其它UR |
| 302 | Found | 臨時(shí)移動(dòng)。與301類似。但資源只是臨時(shí)被移動(dòng)??蛻舳藨?yīng)繼續(xù)使用原有URI |
| 303 | See Other | 查看其它地址。與301類似。使用GET和POST請求查看 |
| 404 | Not Found | 請求的資源(網(wǎng)頁等)不存在 |
| 500 | Internal Server Error | 服務(wù)器內(nèi)部錯(cuò)誤,無法完成請求 |
使用301跳轉(zhuǎn)的場景
1)域名到期不想續(xù)費(fèi)(或者發(fā)現(xiàn)了更適合網(wǎng)站的域名),想換個(gè)域名。
2)在搜索引擎的搜索結(jié)果中出現(xiàn)了不帶www的域名,而帶www的域名卻沒有收錄,這個(gè)時(shí)候可以用301重定向來告訴搜索引擎我們目標(biāo)的域名是哪一個(gè)。
3)空間服務(wù)器不穩(wěn)定,換空間的時(shí)候。
ajax
Ajax(Asynchronous JavaScript And XML),異步 JavaScript 和 XML,用于異步請求數(shù)據(jù),在不刷新網(wǎng)頁的情況下更新頁面數(shù)據(jù),提升用戶體驗(yàn)

3.優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 不刷新頁面的情況下更新數(shù)據(jù)
- 使用異步的方式與服務(wù)器通信,不打斷用戶的操作
- 可將一些后端的工作移到前端,減少服務(wù)器與帶寬的負(fù)擔(dān)
- Ajax使得界面與應(yīng)用分離,也就是數(shù)據(jù)與呈現(xiàn)分離
缺點(diǎn) - AJAX技術(shù)給用戶帶來很好的用戶體驗(yàn)但是會(huì)暴露比以前更多的數(shù)據(jù)和服務(wù)器邏輯
- 對搜索引擎支持較弱
4.實(shí)現(xiàn)
核心XMLHttpRequest (簡稱XHR),可以不刷新界面獲取更新數(shù)據(jù),老版本的IE里使用ActiveXObject
readyState存有XMLHttpRequest的狀態(tài)。從 0 到 4 發(fā)生變化。
0: 請求未初始化
1: 服務(wù)器連接已建立
2: 請求已接收
3: 請求處理中
4: 請求已完成,且響應(yīng)已就緒
status
200: "OK"
404: 未找到頁面
當(dāng) readyState 等于 4 且狀態(tài)為 200 時(shí),表示響應(yīng)已就緒:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ajax</title>
</head>
<body>
<div id="myDiv"><h2>Let Ajax change this text</h2></div>
<button onclick="loadXMLDoc()">通過Ajax改變內(nèi)容</button>
<script>
function loadXMLDoc() {
var xmlhttp;
if(window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
// 每當(dāng) readyState 屬性改變時(shí),就會(huì)調(diào)用該函數(shù)
xmlhttp.onreadstatechange = function() {
if(xmlhttp.readystate == 4 && xmlhttp.status == 200) {
document.getElementById('myDiv').innerHTML = xmlhttp.responseText;
}
}
xmlhttp.open('GET', '/ajax/test1.txt', true);
xmlhttp.send(); // 將請求發(fā)送到服務(wù)器
}
</script>
</body>
</html>
5.axios
Axios 是一個(gè)基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中
- 從瀏覽器中創(chuàng)建
XMLHttpRequests - 從 node.js 創(chuàng)建
http請求 - 支持
PromiseAPI - 攔截請求和響應(yīng)
- 轉(zhuǎn)換請求數(shù)據(jù)和響應(yīng)數(shù)據(jù)
- 取消請求
- 自動(dòng)轉(zhuǎn)換 JSON 數(shù)據(jù)
- 客戶端支持防御
XSRF
get參數(shù):{params:{}}, post參數(shù):{}
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
瀏覽器數(shù)據(jù)存儲(chǔ)
存儲(chǔ)的方式有:
cookie,localstorage,sessionstorage

cookie的弊端:
1.每次請求都會(huì)攜帶cookie里面的信息,增加流量的消耗
2.明文存儲(chǔ)不安全
3.用戶登錄之后,關(guān)閉頁面,重新打開之后為什么還能獲取之前的用戶信息?
用戶登錄成功之后后臺(tái)會(huì)隨機(jī)生成一個(gè)用戶登錄信息(sessionId),并將登錄信息存放在cookie中,下次訪問頁面時(shí),服務(wù)端會(huì)先去cookie中獲取sessionId,并判斷其真實(shí)有效性(可以用來處理單點(diǎn)登錄)
跨域
1.跨域出現(xiàn)的原因:瀏覽器的同源策略(同源策略是瀏覽器的安全策略)
- 同源策略是瀏覽器的一個(gè)安全功能,不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能讀寫對方資源。
- 是用于隔離潛在惡意文件的重要安全機(jī)制。
- 同源(協(xié)議、域名、端口號(hào)相同)
| 當(dāng)前頁面url | 被請求頁面url | 是否跨域 | 原因 |
|---|---|---|---|
| http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(協(xié)議、域名、端口號(hào)相同) |
| http://www.test.com/ | https://www.test.com/index.html | 跨域 | 協(xié)議不同(http/https) |
| http://www.test.com/ | http://www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
| http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同(www/blog) |
| http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 | 端口號(hào)不同(8080/7001) |
2.非同源限制
- 無法讀取非同源網(wǎng)頁的 Cookie、LocalStorage 和 IndexedDB
- 無法接觸非同源網(wǎng)頁的 DOM
- 無法向非同源地址發(fā)送 AJAX 請求
3.跨域解決方法
(1) 設(shè)置document.domain解決無法讀取非同源網(wǎng)頁的 Cookie問題
因?yàn)闉g覽器是通過document.domain屬性來檢查兩個(gè)頁面是否同源,因此只要通過設(shè)置相同的document.domain,兩個(gè)頁面就可以共享Cookie(此方案僅限主域相同,子域不同的跨域應(yīng)用場景。)
// 兩個(gè)頁面都設(shè)置
document.domain = 'test.com';
(2) 跨文檔通信 API:window.postMessage()
調(diào)用postMessage方法實(shí)現(xiàn)父窗口http://test1.com向子窗口http://test2.com發(fā)消息(子窗口同樣可以通過該方法發(fā)送消息給父窗口)
它可用于解決以下方面的問題:
- 頁面和其打開的新窗口的數(shù)據(jù)傳遞
- 多窗口之間消息傳遞
- 頁面與嵌套的iframe消息傳遞
- 上面三個(gè)場景的跨域數(shù)據(jù)傳遞
// 父窗口打開一個(gè)子窗口
var openWindow = window.open('http://test2.com', 'title');
// 父窗口向子窗口發(fā)消息(第一個(gè)參數(shù)代表發(fā)送的內(nèi)容,第二個(gè)參數(shù)代表接收消息窗口的url)
openWindow.postMessage('Nice to meet you!', 'http://test2.com');
調(diào)用message事件,監(jiān)聽對方發(fā)送的消息
// 監(jiān)聽 message 消息
window.addEventListener('message', function (e) {
console.log(e.source); // e.source 發(fā)送消息的窗口
console.log(e.origin); // e.origin 消息發(fā)向的網(wǎng)址
console.log(e.data); // e.data 發(fā)送的消息
},false);
(3) JSONP
JSONP 是服務(wù)器與客戶端跨源通信的常用方法。最大特點(diǎn)就是簡單適用,兼容性好(兼容低版本IE),缺點(diǎn)是只支持get請求,不支持post請求。
核心思想:網(wǎng)頁通過添加一個(gè)<script>元素,向服務(wù)器請求 JSON 數(shù)據(jù),服務(wù)器收到請求后,將數(shù)據(jù)放在一個(gè)指定名字的回調(diào)函數(shù)的參數(shù)位置傳回來。
- 原生實(shí)現(xiàn):
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服務(wù)器test.com發(fā)出請求,該請求的查詢字符串有一個(gè)callback參數(shù),用來指定回調(diào)函數(shù)的名字
// 處理服務(wù)器返回回調(diào)函數(shù)的數(shù)據(jù)
<script type="text/javascript">
function dosomething(res){
// 處理獲得的數(shù)據(jù)
console.log(res.data)
}
</script>
- jQuery ajax
$.ajax({
url: 'http://www.test.com:8080/login',
type: 'get',
dataType: 'jsonp', // 請求方式為jsonp
jsonpCallback: "handleCallback", // 自定義回調(diào)函數(shù)名
data: {}
});
- Vue.js
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
(4) 配置webpack代理解決跨域
proxyTable: {
'/api': {
target: 'http://beta-pvg.goms.com.cn',
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api': ''
}
}
},
4.CORS
CORS 是跨域資源分享(Cross-Origin Resource Sharing)的縮寫。它是 W3C 標(biāo)準(zhǔn),屬于跨源 AJAX 請求的根本解決方法。
(1) 普通跨域請求:只需服務(wù)器端設(shè)置Access-Control-Allow-Origin
(2) 帶cookie跨域請求:前后端都需要進(jìn)行設(shè)置
【前端設(shè)置】根據(jù)xhr.withCredentials字段判斷是否帶有cookie
web安全問題
1.SQL注入(SQL Injection)
定義
由于程序中對用戶輸入檢查不嚴(yán)格,用戶可以提交一段數(shù)據(jù)庫查詢代碼,根據(jù)程序返回的結(jié)果,獲得某些他想得知的數(shù)據(jù),這就是所謂的SQL Injection,即SQL注入。
原因分析
其本質(zhì)是對于輸入檢查不充分,導(dǎo)致SQL語句將用戶提交的非法數(shù)據(jù)當(dāng)作語句的一部分來執(zhí)行。
風(fēng)險(xiǎn)
SQL盲注:如果系統(tǒng)屏蔽了詳細(xì)的錯(cuò)誤信息,那么對攻擊者而言就是盲注入,可能會(huì)查看、修改或刪除數(shù)據(jù)庫條目和表
使用SQL注入的認(rèn)證旁路:可能會(huì)繞開 Web 應(yīng)用程序的認(rèn)證機(jī)制
例子

預(yù)防措施
- 不要使用動(dòng)態(tài)SQL
避免將用戶提供的輸入直接放入SQL語句中;最好使用準(zhǔn)備好的語句和參數(shù)化查詢,這樣更安全。 - 不要將敏感數(shù)據(jù)保留在純文本中
加密存儲(chǔ)在數(shù)據(jù)庫中的私有/機(jī)密數(shù)據(jù);這樣可以提供了另一級(jí)保護(hù),以防攻擊者成功地排出敏感數(shù)據(jù)。 - 限制數(shù)據(jù)庫權(quán)限和特權(quán)
將數(shù)據(jù)庫用戶的功能設(shè)置為最低要求;這將限制攻擊者在設(shè)法獲取訪問權(quán)限時(shí)可以執(zhí)行的操作。 - 避免直接向用戶顯示數(shù)據(jù)庫錯(cuò)誤
攻擊者可以使用這些錯(cuò)誤消息來獲取有關(guān)數(shù)據(jù)庫的信息。 - 對訪問數(shù)據(jù)庫的Web應(yīng)用程序使用Web應(yīng)用程序防火墻(WAF)
這為面向Web的應(yīng)用程序提供了保護(hù),它可以幫助識(shí)別SQL注入嘗試;根據(jù)設(shè)置,它還可以幫助防止SQL注入嘗試到達(dá)應(yīng)用程序(以及數(shù)據(jù)庫)。 - 定期測試與數(shù)據(jù)庫交互的Web應(yīng)用程序
這樣做可以幫助捕獲可能允許SQL注入的新錯(cuò)誤或回歸。 - 將數(shù)據(jù)庫更新為最新的可用修補(bǔ)程序
這可以防止攻擊者利用舊版本中存在的已知弱點(diǎn)/錯(cuò)誤。
2.跨站腳本攻擊(XSS)
定義
XSS攻擊是Web攻擊中最常見的攻擊方法之一,它是通過對網(wǎng)頁注入可執(zhí)行代碼且成功地被瀏覽器 執(zhí)行,達(dá)到攻擊的目的
形成了一次有效XSS攻擊,一旦攻擊成功,它可以獲取用戶的聯(lián)系人列表,然后向聯(lián)系人發(fā)送虛假詐騙信息,可以刪除用戶的日志等等,有時(shí)候還和其他攻擊方式同時(shí)實(shí) 施比如SQL注入攻擊服務(wù)器和數(shù)據(jù)庫、Click劫持、相對鏈接劫持等實(shí)施釣魚,它帶來的危害是巨 大的,是web安全的頭號(hào)大敵。
分類
XSS反射型攻擊,惡意代碼并沒有保存在目標(biāo)網(wǎng)站,通過引誘用戶點(diǎn)擊一個(gè)鏈接到目標(biāo)網(wǎng)站的惡意鏈接來實(shí)施攻擊的。
XSS存儲(chǔ)型攻擊,惡意代碼被保存到目標(biāo)網(wǎng)站的服務(wù)器中,這種攻擊具有較強(qiáng)的穩(wěn)定性和持久性,比較常見場景是在博客,論壇等社交網(wǎng)站上,但OA系統(tǒng),和CRM系統(tǒng)上也能看到它身影,比如:某CRM系統(tǒng)的客戶投訴功能上存在XSS存儲(chǔ)型漏洞,黑客提交了惡意攻擊代碼,當(dāng)系統(tǒng)管理員查看投訴信息時(shí)惡意代碼執(zhí)行,竊取了客戶的資料,然而管理員毫不知情,這就是典型的XSS存儲(chǔ)型攻擊。
例子:

對用戶提交的所有內(nèi)容進(jìn)行過濾,對url中的參數(shù)進(jìn)行過濾,過濾掉會(huì)導(dǎo)致腳本執(zhí)行的相關(guān)內(nèi)容;
對動(dòng)態(tài)輸出到頁面的內(nèi)容進(jìn)行html編碼,使腳本無法在瀏覽器中執(zhí)行。
對輸入的內(nèi)容進(jìn)行過濾,可以分為黑名單過濾和白名單過濾。黑名單過濾雖然可以攔截大部分的XSS攻擊,但是還是存在被繞過的風(fēng)險(xiǎn)。白名單過濾雖然可以基本杜絕XSS攻擊,但是真實(shí)環(huán)境中一般是不能進(jìn)行如此嚴(yán)格的白名單過濾的。
對輸出進(jìn)行html編碼,就是通過函數(shù),將用戶的輸入的數(shù)據(jù)進(jìn)行html編碼,使其不能作為腳本運(yùn)行。
如下,是使用php中的htmlspecialchars函數(shù)對用戶輸入的name參數(shù)進(jìn)行html編碼,將其轉(zhuǎn)換為html實(shí)體
#使用htmlspecialchars函數(shù)對用戶輸入的name參數(shù)進(jìn)行html編碼,將其轉(zhuǎn)換為html實(shí)體
$name = htmlspecialchars( $_GET[ 'name' ] );
如下,圖一是沒有進(jìn)行html編碼的,圖2是進(jìn)行了html編碼的。經(jīng)過html編碼后script標(biāo)簽被當(dāng)成了html實(shí)體。


我們還可以服務(wù)端設(shè)置會(huì)話Cookie的HTTP Only屬性,這樣,客戶端的JS腳本就不能獲取Cookie信息了
3.跨站請求偽造(CSRF/XSRF )
在瀏覽器的同源策略的約束下,對于跨域資源交互進(jìn)行處理時(shí),【通常允許跨域資源嵌入(Cross-origin embedding)】
下面是常見跨域資源嵌入示例:
- <script src="..."></script>標(biāo)簽嵌入跨域腳本。語法錯(cuò)誤信息只能在同源腳本中捕捉到。
- <img>嵌入圖片。支持的圖片格式包括PNG,JPEG,GIF,BMP,SVG,...
- <video> 和 <audio>嵌入多媒體資源。
CSRF 攻擊的原理,就是利用由于瀏覽器的同源策略對以上嵌入資源不做限制的行為進(jìn)行跨站請求偽造的。
3.1 CSRF 攻擊原理

- 用戶瀏覽位于目標(biāo)服務(wù)器 A 的網(wǎng)站。并通過登錄驗(yàn)證。
- 獲取到 cookie_session_id,保存到瀏覽器 cookie 中
- 在未登出服務(wù)器 A ,并在 session_id 失效前用戶瀏覽位于 hacked server B 上的網(wǎng)站。
- server B 網(wǎng)站中的<img src = "http://www.altoromutual.com/bank/transfer.aspx?creditAccount=1001160141&transferAmount=1000">嵌入資源起了作用,迫使用戶訪問目標(biāo)服務(wù)器 A
- 由于用戶未登出服務(wù)器 A 并且 sessionId 未失效,請求通過驗(yàn)證,非法請求被執(zhí)行
3.2 如何防御 CSRF
- referer 驗(yàn)證解決方案。
最簡單的方法依賴于瀏覽器引用頁頭部。大多數(shù)瀏覽器會(huì)告訴 Web 服務(wù)器,哪個(gè)頁面發(fā)送了請求。如:
POST /bank/transfer.aspx HTTP/1.1
Referer: http://evilsite.com/myevilblog
User-Agent: Mozilla/4....
Host: www.altoromutual.com
Content-Length: 42
Cookie: SessionId=x3q2v0qpjc0n1c55mf35fxid;
不少站點(diǎn)通過 referer 驗(yàn)證來防止盜鏈本站圖片資源。
不過由于 http 頭在某些版本的瀏覽器上存在可被篡改的可能性,所以這個(gè)解決方案并不完善
- Token 解決方案
令牌解決方案向表單添加一個(gè)參數(shù),讓表單在用戶注銷時(shí)或一個(gè)超時(shí)期限結(jié)束后過期
<form id="transferForm" action="https://www.altoromutual.com/bank/transfer.aspx" method="post">
Enter the credit account:
<input type="text" name="creditAccount" value="">
Enter the transfer amount:
<input type="text" name="transferAmount" value="">
<input type="hidden" name="xsrftoken" value="JKBS38633jjhg0987PPll">
<input type="submit" value="Submit">
</form>
或者將服務(wù)端動(dòng)態(tài)生成的 Token 加入到 自定義 http 請求頭參數(shù)中
POST /bank/transfer.aspx HTTP/1.1
Referer: https://www.altoromutual.com/bank
xsrftoken: JKBS38633jjhg0987PPll
User-Agent: Mozilla/4....
Host: www.altoromutual.com
Content-Length: 42
Cookie: SessionId=x3q2v0qpjc0n1c55mf35fxid;
creditAccount=1001160141&transferAmount=10
token 解決方案的問題在于前后端代碼的巨大變更。并且每一步都動(dòng)態(tài)生成 token 并且對 token 進(jìn)行驗(yàn)證的話,也會(huì)造成額外的資源開銷。可以嘗試在關(guān)鍵性操作的地方再加上 token 驗(yàn)證邏輯。但是,token 驗(yàn)證所帶來的前后端代碼的變動(dòng)所帶來的消耗,則需要慎重考慮
- userId 解決方案
相對于 token 這樣的需要前后端邏輯作出改動(dòng),以及造成額外資源開銷的方式以外還有一種小巧的防范措施。就是用戶每次請求關(guān)鍵性數(shù)據(jù)時(shí),后端接口在設(shè)計(jì)時(shí)都需要用戶提交相關(guān)的 userId。userId 可以存儲(chǔ)到瀏覽器的 localStorage 中,這樣便能進(jìn)一步提高接口安全性,防止跨站請求偽造。



