一妄讯、 原型和原型鏈
- 所有的引用類型(數(shù)組孩锡、函數(shù)、對(duì)象)可以自由擴(kuò)展屬性(除null以外)亥贸。
- 引用類型有一個(gè)_ _ proto_ _屬性(也叫隱式原型躬窜,它是一個(gè)普通的對(duì)象)。
- 函數(shù)有一個(gè)prototype屬性(這也叫顯式原型炕置,它也是一個(gè)普通的對(duì)象)荣挨。
- 引用類型的_ _ proto_ _指向它構(gòu)造函數(shù)的prototype。
- 當(dāng)試圖得到一個(gè)對(duì)象的屬性時(shí)朴摊,如果這個(gè)對(duì)象本身不存在這個(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的方法,可以獲得對(duì)象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
-
原型鏈:利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法仍劈。然后層層遞進(jìn)厕倍,就構(gòu)成了實(shí)例與原型的鏈條。
image.png new操作符具體干了什么呢贩疙?
1讹弯、創(chuàng)建一個(gè)空對(duì)象况既,并且 this 變量引用該對(duì)象,同時(shí)還繼承了該函數(shù)的原型组民。
2棒仍、屬性和方法被加入到 this 引用的對(duì)象中。
3臭胜、新創(chuàng)建的對(duì)象由 this 所引用莫其,并且最后隱式的返回 this 。
閉包
閉包是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
1.優(yōu)點(diǎn):
- 變量長(zhǎng)期駐扎在內(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;
堆和棧
堆和棧在不同的場(chǎng)景下柄慰,有不同的含義:
(1)程序內(nèi)存布局場(chǎng)景下,堆與棧表示兩種內(nèi)存管理方式税娜;
(2)數(shù)據(jù)結(jié)構(gòu)場(chǎng)景下坐搔,堆與棧表示兩種常用的數(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對(duì)象
image.png
補(bǔ)充:
基本類型: undefined,boolean弧岳,number凳忙,string业踏,null.
引用類型:object、Array涧卵、RegExp勤家、Date、Function柳恐、特殊的基本包裝類型(String伐脖、Number、Boolean)以及單體內(nèi)置對(duì)象(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è)基本類型有其對(duì)應(yīng)的包裝對(duì)象,包裝對(duì)象有其對(duì)應(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 找到對(duì)應(yīng)的包裝對(duì)象類型懒震,然后通過包裝對(duì)象創(chuàng)建出一個(gè)和基本類型值相同的對(duì)象
var s2 = _str.chaAt(0); // 2 然后這個(gè)對(duì)象就可以調(diào)用包裝對(duì)象下的方法,并且返回結(jié)給s2.
_str = null; // 3 之后這個(gè)臨時(shí)創(chuàng)建的對(duì)象就被銷毀了嗤详, str =null;
)
alert(s2);//h
alert(str);//hello
注意這一瞬間个扰,我們并沒有改變?cè)址畇tr的值,只是新建了一個(gè)_str對(duì)象得出結(jié)果葱色,賦值給s2
引用類型創(chuàng)建的對(duì)象在執(zhí)行期間一直存在递宅,包裝類型創(chuàng)建的對(duì)象只存在了一瞬間
- 淺拷貝和深拷貝
- 淺拷貝,拷貝了對(duì)象的引用,原對(duì)象變化時(shí)办龄,拷貝對(duì)象變化
- 深拷貝烘绽,拷貝了對(duì)象的值,原對(duì)象變化時(shí)土榴,拷貝對(duì)象不變
深拷貝的方法 -
obj2 = Object.assign({}, obj1)
創(chuàng)建了一個(gè)空對(duì)象诀姚,把obj1屬性復(fù)制過去,obj1改變不會(huì)影響obj2 -
obj2 = JSON.parse(JSON.stringify(obj1))
JSON.stringify把對(duì)象轉(zhuǎn)成字符串玷禽,JSON.parser把字符串轉(zhuǎn)成新對(duì)象 - 遞歸拷貝
瀏覽器渲染原理
1.瀏覽器渲染過程
- HTML文檔解析成DOM樹 赫段。
- 處理CSS標(biāo)記,構(gòu)成層疊樣式表模型CSSOM(CSS Object Model)
- 將DOM和CSSOM合并為渲染樹(rendering tree)矢赁。
在這一過程中糯笙,不是簡(jiǎn)單的將兩者合并就行了。渲染樹只會(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)的確切位置和尺寸,所有相對(duì)測(cè)量值都將轉(zhuǎn)換成屏幕上的絕對(duì)像素境肾。
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)行交互操作,就勢(shì)必會(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)的字體等买优。對(duì)于這些操作,瀏覽器會(huì)馬上進(jìn)行reflow挺举。
3.3 引起reflow
現(xiàn)代瀏覽器會(huì)對(duì)回流做優(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ā)回流重繪)
- 避免多次讀取某些屬性
- 通過絕對(duì)位移將復(fù)雜的節(jié)點(diǎn)元素脫離文檔流吼过,形成新的Render Layer锐秦,降低回流成本
4. 幾條關(guān)于優(yōu)化渲染效率的建議
- 合法地去書寫HTML和CSS ,且不要忘了文檔編碼類型盗忱。
- 樣式文件應(yīng)當(dāng)在head標(biāo)簽中酱床,而腳本文件在body結(jié)束前,這樣可以防止阻塞的方式趟佃。
- 簡(jiǎn)化并優(yōu)化CSS選擇器斤葱,盡量將嵌套層減少到最小慷垮。
- DOM 的多個(gè)讀操作(或多個(gè)寫操作),應(yīng)該放在一起揍堕。不要兩個(gè)讀操作之間,加入一個(gè)寫操作汤纸。
- 如果某個(gè)樣式是通過重排得到的衩茸,那么最好緩存結(jié)果。避免下一次用到的時(shí)候贮泞,瀏覽器又要重排
- 不要一條條地改變樣式楞慈,而要通過改變class,或者csstext屬性啃擦,一次性地改變樣式囊蓝。
- 盡量用transform來做形變和位移
- 盡量使用離線DOM,而不是真實(shí)的網(wǎng)面DOM令蛉,來改變?cè)貥邮骄鬯1热纾僮鱀ocument Fragment對(duì)象珠叔,完成后再把這個(gè)對(duì)象加入DOM蝎宇。再比如,使用cloneNode()方法祷安,在克隆的節(jié)點(diǎn)上進(jìn)行操作姥芥,然后再用克隆的節(jié)點(diǎn)替換原始節(jié)點(diǎn)。
- 先將元素設(shè)為display: none(需要1次重排和重繪)汇鞭,然后對(duì)這個(gè)節(jié)點(diǎn)進(jìn)行100次操作凉唐,最后再恢復(fù)顯示(需要1次重排和重繪)。這樣一來霍骄,你就用兩次重新渲染台囱,取代了可能高達(dá)100次的重新渲染。
- position屬性為absolute或fixed的元素腕巡,重排的開銷會(huì)比較小玄坦,因?yàn)椴挥每紤]它對(duì)其他元素的影響
- 只在必要的時(shí)候,才將元素的display屬性為可見绘沉,因?yàn)椴豢梢姷脑夭挥绊懼嘏藕椭乩L煎楣。另外,visibility : hidden的元素只對(duì)重繪有影響车伞,不影響重排择懂。
- 使用window.requestAnimationFrame()、window.requestIdleCallback()這兩個(gè)方法調(diào)節(jié)重新渲染另玖。
http狀態(tài)碼
分類 | 分類描述 |
---|---|
1** | 信息困曙,服務(wù)器收到請(qǐng)求表伦,需要請(qǐng)求者繼續(xù)執(zhí)行操作 |
2** | 成功,操作被成功接收并處理 |
3** | 重定向慷丽,需要進(jìn)一步的操作以完成請(qǐng)求 |
4** | 客戶端錯(cuò)誤蹦哼,請(qǐng)求包含語法錯(cuò)誤或無法完成請(qǐng)求 |
5** | 服務(wù)器錯(cuò)誤,服務(wù)器在處理請(qǐng)求的過程中發(fā)生了錯(cuò)誤 |
狀態(tài)碼 | 狀態(tài)碼英文名稱 | 中文描述 |
---|---|---|
200 | OK | 請(qǐng)求成功要糊。 |
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請(qǐng)求查看 |
404 | Not Found | 請(qǐng)求的資源(網(wǎng)頁等)不存在 |
500 | Internal Server Error | 服務(wù)器內(nèi)部錯(cuò)誤,無法完成請(qǐng)求 |
使用301跳轉(zhuǎn)的場(chǎng)景
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,用于異步請(qǐng)求數(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ù)器邏輯
- 對(duì)搜索引擎支持較弱
4.實(shí)現(xiàn)
核心XMLHttpRequest (簡(jiǎn)稱XHR),可以不刷新界面獲取更新數(shù)據(jù)扮碧,老版本的IE里使用ActiveXObject
readyState
存有XMLHttpRequest
的狀態(tài)趟章。從 0 到 4 發(fā)生變化。
0: 請(qǐng)求未初始化
1: 服務(wù)器連接已建立
2: 請(qǐng)求已接收
3: 請(qǐng)求處理中
4: 請(qǐng)求已完成慎王,且響應(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(); // 將請(qǐng)求發(fā)送到服務(wù)器
}
</script>
</body>
</html>
5.axios
Axios 是一個(gè)基于 promise 的 HTTP 庫赖淤,可以用在瀏覽器和 node.js 中
- 從瀏覽器中創(chuàng)建
XMLHttpRequests
- 從 node.js 創(chuàng)建
http
請(qǐng)求 - 支持
Promise
API - 攔截請(qǐng)求和響應(yīng)
- 轉(zhuǎn)換請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù)
- 取消請(qǐng)求
- 自動(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.每次請(qǐng)求都會(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)的情況下病袄,不能讀寫對(duì)方資源。
- 是用于隔離潛在惡意文件的重要安全機(jī)制赘阀。
- 同源(協(xié)議、域名脑奠、端口號(hào)相同)
當(dāng)前頁面url | 被請(qǐng)求頁面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 請(qǐng)求
3.跨域解決方法
(1) 設(shè)置document.domain解決無法讀取非同源網(wǎng)頁的 Cookie問題
因?yàn)闉g覽器是通過document.domain屬性來檢查兩個(gè)頁面是否同源轰豆,因此只要通過設(shè)置相同的document.domain,兩個(gè)頁面就可以共享Cookie(此方案僅限主域相同齿诞,子域不同的跨域應(yīng)用場(chǎ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è)場(chǎng)景的跨域數(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)聽對(duì)方發(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)就是簡(jiǎn)單適用,兼容性好(兼容低版本IE)但汞,缺點(diǎn)是只支持get請(qǐng)求宿刮,不支持post請(qǐng)求。
核心思想:網(wǎng)頁通過添加一個(gè)<script>元素私蕾,向服務(wù)器請(qǐng)求 JSON 數(shù)據(jù)僵缺,服務(wù)器收到請(qǐng)求后,將數(shù)據(jù)放在一個(gè)指定名字的回調(diào)函數(shù)的參數(shù)位置傳回來踩叭。
- 原生實(shí)現(xiàn):
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服務(wù)器test.com發(fā)出請(qǐng)求磕潮,該請(qǐng)求的查詢字符串有一個(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', // 請(qǐng)求方式為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 請(qǐng)求的根本解決方法。
(1) 普通跨域請(qǐng)求:只需服務(wù)器端設(shè)置Access-Control-Allow-Origin
(2) 帶cookie跨域請(qǐng)求:前后端都需要進(jìn)行設(shè)置
【前端設(shè)置】根據(jù)xhr.withCredentials字段判斷是否帶有cookie
web安全問題
1.SQL注入(SQL Injection)
定義
由于程序中對(duì)用戶輸入檢查不嚴(yán)格嗤疯,用戶可以提交一段數(shù)據(jù)庫查詢代碼冤今,根據(jù)程序返回的結(jié)果,獲得某些他想得知的數(shù)據(jù)
茂缚,這就是所謂的SQL Injection戏罢,即SQL注入屋谭。
原因分析
其本質(zhì)是對(duì)于輸入檢查不充分,導(dǎo)致SQL語句將用戶提交的非法數(shù)據(jù)當(dāng)作語句的一部分來執(zhí)行龟糕。
風(fēng)險(xiǎn)
SQL盲注:如果系統(tǒng)屏蔽了詳細(xì)的錯(cuò)誤信息桐磁,那么對(duì)攻擊者而言就是盲注入,可能會(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ù)庫的信息溪窒。 - 對(duì)訪問數(shù)據(jù)庫的Web應(yīng)用程序使用Web應(yīng)用程序防火墻(WAF)
這為面向Web的應(yīng)用程序提供了保護(hù)坤塞,它可以幫助識(shí)別SQL注入嘗試;根據(jù)設(shè)置澈蚌,它還可以幫助防止SQL注入嘗試到達(dá)應(yīng)用程序(以及數(shù)據(jù)庫)摹芙。 - 定期測(cè)試與數(shù)據(jù)庫交互的Web應(yīng)用程序
這樣做可以幫助捕獲可能允許SQL注入的新錯(cuò)誤或回歸。 - 將數(shù)據(jù)庫更新為最新的可用修補(bǔ)程序
這可以防止攻擊者利用舊版本中存在的已知弱點(diǎn)/錯(cuò)誤宛瞄。
2.跨站腳本攻擊(XSS)
定義
XSS攻擊是Web攻擊中最常見的攻擊方法之一浮禾,它是通過對(duì)網(wǎng)頁注入可執(zhí)行代碼且成功地被瀏覽器 執(zhí)行,達(dá)到攻擊的目的
形成了一次有效XSS攻擊坛悉,一旦攻擊成功伐厌,它可以獲取用戶的聯(lián)系人列表,然后向聯(lián)系人發(fā)送虛假詐騙信息裸影,可以刪除用戶的日志等等挣轨,有時(shí)候還和其他攻擊方式同時(shí)實(shí) 施比如SQL注入攻擊服務(wù)器和數(shù)據(jù)庫、Click劫持轩猩、相對(duì)鏈接劫持等實(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)定性和持久性焦影,比較常見場(chǎng)景是在博客车遂,論壇等社交網(wǎng)站上封断,但OA系統(tǒng),和CRM系統(tǒng)上也能看到它身影舶担,比如:某CRM系統(tǒng)的客戶投訴功能上存在XSS存儲(chǔ)型漏洞坡疼,黑客提交了惡意攻擊代碼,當(dāng)系統(tǒng)管理員查看投訴信息時(shí)惡意代碼執(zhí)行衣陶,竊取了客戶的資料柄瑰,然而管理員毫不知情,這就是典型的XSS存儲(chǔ)型攻擊剪况。
例子:
對(duì)用戶提交的所有內(nèi)容進(jìn)行過濾教沾,對(duì)url中的參數(shù)進(jìn)行過濾,過濾掉會(huì)導(dǎo)致腳本執(zhí)行的相關(guān)內(nèi)容译断;
對(duì)動(dòng)態(tài)輸出到頁面的內(nèi)容進(jìn)行html編碼详囤,使腳本無法在瀏覽器中執(zhí)行。
對(duì)輸入的內(nèi)容進(jìn)行過濾镐作,可以分為黑名單過濾和白名單過濾。黑名單過濾雖然可以攔截大部分的XSS攻擊隆箩,但是還是存在被繞過的風(fēng)險(xiǎn)该贾。白名單過濾雖然可以基本杜絕XSS攻擊,但是真實(shí)環(huán)境中一般是不能進(jìn)行如此嚴(yán)格的白名單過濾的捌臊。
對(duì)輸出進(jìn)行html編碼杨蛋,就是通過函數(shù),將用戶的輸入的數(shù)據(jù)進(jìn)行html編碼理澎,使其不能作為腳本運(yùn)行逞力。
如下,是使用php中的htmlspecialchars函數(shù)對(duì)用戶輸入的name參數(shù)進(jìn)行html編碼糠爬,將其轉(zhuǎn)換為html實(shí)體
#使用htmlspecialchars函數(shù)對(duì)用戶輸入的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.跨站請(qǐng)求偽造(CSRF/XSRF )
在瀏覽器的同源策略的約束下,對(duì)于跨域資源交互進(jìn)行處理時(shí),【通常允許跨域資源嵌入(Cross-origin embedding)】
下面是常見跨域資源嵌入示例:
- <script src="..."></script>標(biāo)簽嵌入跨域腳本。語法錯(cuò)誤信息只能在同源腳本中捕捉到豺裆。
- <img>嵌入圖片逸尖。支持的圖片格式包括PNG,JPEG,GIF,BMP,SVG,...
- <video> 和 <audio>嵌入多媒體資源知染。
CSRF 攻擊的原理魔招,就是利用由于瀏覽器的同源策略對(duì)以上嵌入資源不做限制的行為進(jìn)行跨站請(qǐng)求偽造的舱污。
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 未失效,請(qǐng)求通過驗(yàn)證串稀,非法請(qǐng)求被執(zhí)行
3.2 如何防御 CSRF
- referer 驗(yàn)證解決方案除抛。
最簡(jiǎn)單的方法依賴于瀏覽器引用頁頭部。大多數(shù)瀏覽器會(huì)告訴 Web 服務(wù)器母截,哪個(gè)頁面發(fā)送了請(qǐng)求到忽。如:
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 請(qǐng)求頭參數(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 并且對(duì) token 進(jìn)行驗(yàn)證的話华烟,也會(huì)造成額外的資源開銷翩迈。可以嘗試在關(guān)鍵性操作的地方再加上 token 驗(yàn)證邏輯盔夜。但是负饲,token 驗(yàn)證所帶來的前后端代碼的變動(dòng)所帶來的消耗,則需要慎重考慮
- userId 解決方案
相對(duì)于 token 這樣的需要前后端邏輯作出改動(dòng)喂链,以及造成額外資源開銷的方式以外還有一種小巧的防范措施返十。就是用戶每次請(qǐng)求關(guān)鍵性數(shù)據(jù)時(shí),后端接口在設(shè)計(jì)時(shí)都需要用戶提交相關(guān)的 userId椭微。userId 可以存儲(chǔ)到瀏覽器的 localStorage 中洞坑,這樣便能進(jìn)一步提高接口安全性,防止跨站請(qǐng)求偽造蝇率。