js相關(guān)學(xué)習(xí)

一、 原型和原型鏈

  • 所有的引用類型(數(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)。

  1. 這里說的是內(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)建的對象只存在了一瞬間

  1. 淺拷貝和深拷貝
  • 淺拷貝,拷貝了對象的引用,原對象變化時(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)


image.png

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


111.png

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ī)制
例子

image.png

預(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ǔ)型攻擊。
例子:

image.png

對用戶提交的所有內(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í)體。


image.png

image.png

我們還可以服務(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 攻擊原理

image.png
  • 用戶瀏覽位于目標(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)一步提高接口安全性,防止跨站請求偽造。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容