[toc]
## usage(4步)
Ajax(async javascript and xml)
1)首先創建一個Ajax對象
```
var xhr = new XMLHttpRequest;
```
2)打開我們需要請求數據的那個文件地址
```
xhr.open("get","./data.txt",true); //true為異步
```
3)監聽請求的狀態
```
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
var val = xhr.responseText;
data = utils.jsonParse(val);
}
};
```
4)發送請求
```
xhr.send(null);
```

設置請求頭必須在open之后以及send之前


## xml
可擴展的標記語言
>作用是用來存儲數據的(通過自己擴展的標記名清晰的展示出來數據結構)

ajax之所以稱之為異步的js和xml,主要原因是,當初最開始用ajax實現客戶端和服務器端數據通信的時候,傳輸的數據格式一般都是xml格式的數據,
但現在我們一般都是基于JSON格來進行數據傳輸的
## 異步的JS
>這里的異步不是說ajax只能基于異步進行請求(雖然建議都是使用異步編程),這里的異步特制的是"局部刷新"
### 局部刷新VS全局刷新

在非完全前后端分離項目中,前端開發只需要完成頁面的制作,并且把一些基礎的人機交互效果使用JS完成即可,頁面中需要動態呈現內容的部分都是交給后臺開發工程師做數據的綁定和基于服務器進行渲染的。
優勢:
1、 動態展示的數據在頁面的源代碼中可以看到,有利于SEO優化推廣
2、從服務器端獲取的結果就已經是最后要呈現的結果了,不需要客戶端做額外的事情,所以頁面加載速度快(前提是服務器端處理的速度夠快,能夠處理過來),所以類似于京東、淘寶這些網站,首屏數據一般都是經由服務器端渲染的
弊端:
1、如果頁面中存在需要實時更新的數據,每一次想要展示最新的數據,頁面都要重新的刷新一次,這樣肯定不行
2、都交給服務器端做數據渲染,服務器端的壓力太大,如果服務器處理不過來,頁面呈現的速度更慢(所以京東淘寶這類網站,除了首屏是服務器端渲染,其它屏一般都是客戶端做數據綁定)
3、 這種模式不利于開發(開發效率低)
---
目前市場上大部分項目都是前后端完全分離的項目
前后端完全分離的項目,頁面中需要動態綁定的數據是交給客戶端完成渲染的
1、想服務器端發送AJAX請求
2、把從服務器端獲取的數據解析處理,拼接成我們需要展示的HTML字符串
3、把拼接好的字符串替換成頁面中某一部分的內容(局部刷新),頁面整體不需要重新架子啊,局部渲染即可
【優勢】
1、我們可以根據需求任意修改頁面中某一部分中的內容(例如實時刷新),整體頁面不刷新,性能好,體驗好(所有表單驗證,需要實時刷新的等需求都要基于AJAX實現)
2、有利于開發,提高開發效率
1)前后端的完全分離,后臺不需要考慮前端如何實現,前端也不需要考慮后臺用什么技術,真正意義上實現了技術的劃分
2)可以同時進行開發:項目開發開始,首先指定前后端數據交互的接口文檔(文檔中包含了,調取那個接口或則那些數據等協議規范),后臺把接口先寫好(目前很多公司也需要前端拿NODE來模擬這些接口),客戶端按照接口調取即可,后臺再次去實現接口功能即可。
【弊端】
1、不利于SERO優化:第一次從服務器端獲取的內容不包含需要的動態綁定的數據,所以頁面的源代碼中沒有這些內容,不利于SEO收錄,后期通過JS添加到頁面中的內容,并不會卸載頁面的源代碼中(是源代碼,不是頁面結構)
2、交由客戶端渲染,首先需要把頁面呈現,然后再通過JS的異步AJAX請求獲取數據,然后數據綁定,瀏覽器在把動態增加的部分重新渲染(無形當中浪費了一些時間,沒有服務器渲染頁面呈現速度快)
## 基于原生JS實現AJAX
```
//=>創建一個AJAX對象
let xhr = new XMLHttpRequest(); //->不兼容IE6及更低版本瀏覽器
更低版本瀏覽器(IE6,ActiveXObject)
//=>打開請求地址(可以理解為一些基礎配置,但是還并沒有發送請求)
xhr.open([method],[url],[async],[username],[user password])
//=>監聽AJAX狀態,獲取響應信息(獲取響應頭信息,獲取響應主體信息)
xhr.onreadystatechange = ()=>{
if(xhr.readyState===4&&xhr.status===200){
let result = xhr.responseText; //=>獲取響應主體中的內容
}
};
//=>發送AJAX請求(括號中傳遞的內容就是請求主體的內容)
xhr.send(null);
```
### 關于open
>xhr.open([method],[url],[async],[user name],[user password])
>
>[HTTP請求方式]
>1、GET系列請求(獲取)
>- get
>- delete:從服務器上刪除某些資源文件
>- head:只想獲取服務器返回的響應頭信息(響應體內容不需要獲取)
>...
>2、POST系列請求(推送)
>- post
>- put:向服務器中增加指定的資源文件(put一般比post還多,但沒有特別明確的要求)
>...
>
>不管哪一種請求方式,客戶端都可以把一些信息傳遞給服務器,服務器也可以把信息返回給客戶端,只是GET系列一般以獲取為主(給的少,拿回來的多),而POST系列一般以推送為主(給的多,拿回來的少)
>1)我們想要獲取一些動態展示的信息,一把使用GET請求,因為只需要向服務器發送請求,告訴服務器端我們想要什么,服務器端就會把需要的數據返回
>2)在實現注冊功能的時候,我們需要把客戶端輸入的信息發送給服務器進行存儲,服務器端一般需要返回成功還是失敗等狀態,此時我們一般都是基于POST請求完成的
#### GET系列請求和POST系列請求,在項目實戰中存在很多的區別
1、GET請求傳遞給服務器的內容一般沒有POST請求傳遞給服務器的多
原因:
GET請求傳遞給服務器的內容一般都是基于`url地址問號傳參`來實現的,而POST請求一般都是基于`設置請求主體`來實現的。
各大瀏覽器都有自己URL最大長度限制(超過會被截取掉):
谷歌:8KB
火狐:7KB
IE:2KB
2、 理論上POST請求通過請求主體傳遞是沒有大小限制的
真實項目中為了保證傳輸的速率,我么也會限制大小(例如:上傳的資料或則圖片我們都會做大小限制)
3、GET請求很容易出現緩存(這個緩存不可控,瀏覽器自己實現的),而POST不會出現緩存(除非自己做特殊處理)
原因:get是通過`url冒號`傳參的,而POST是設置請求主體(設置請求主體不會出現緩存,但是URL傳遞參數就會了)
```
//=>每隔一分鐘重新請求服務器最新的數據,然后展示在頁面中(頁面中某些數據實時刷新)
setTimeout(()=>{
$.ajax({
url:'getList?lx=news',
...
success:function(result){
//=>第一次請求數據回來,間隔一分鐘后,瀏覽器又發送一次請求,但是新發送的請求不管是地址還是傳遞的參數都和第一次一樣,瀏覽器很有可能 會把上一次數據獲取,而不是獲取最新的數據
}
})
},60000)
```
解決方案
```
//=>每隔一分鐘重新請求服務器最新的數據,然后展示在頁面中(頁面中某些數據實時刷新)
setTimeout(()=>{
$.ajax({
url:'getList?lx=news&__='+'Math.random()', //加上隨機字符串 避免從緩存中獲取數據
...
success:function(result){
})
},60000)
```
4、GET請求沒有POST請求安全(POST也并不是十分安全,只是相對安全)
原因:還是因為GET是URL傳參給服務器,有一種比較簡單的黑客技術:URL劫持,也就是可以把客戶端傳遞給服務器的數據劫持掉,導致信息泄露
### 關于URL
請求數據的地址(API地址),真實項目中,后臺開發工程師會編寫一個API文檔,在API文檔中匯總了獲取哪些數據需要使用哪些地址,我們按照文檔操作即可。

### 關于ASYNC
```
ASYNC:異步(SYNC同步),設置當前AJAX請求是異步的還是同步的,不寫默認是異步(TRUE),如果設置為false,則代表當前請求是同步的
```
### 用戶名和密碼
這兩個參數一般不用,如果你請求的URL地址所在的服務器設定了訪問權限,則需要我們提供可通行的用戶名和密碼才可以(一般服務器都是可以云溪匿名訪問的)
開發時記錄開發人員操作可能有用
### xhr下的屬性和方法方法
```
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
var val = xhr.responseText;
data = utils.jsonParse(val);
}
};
```
#### AJAX狀態碼(xhr.readyState):描述當前AJAX操作的狀態的
通過xhr的`__proto__`能找到

>xhr.readyState
>
>0:UNSENT 未發送,只要創建一個AJAX對象,默認值就是0
>1:OPENED 我們已經執行了xhr.open這個操作
>2:HEADERS_RECEIVED 當前AJAX請求已經發送,并且已經接受到服務器端返回的響應頭信息了
>3:LOADING 響應主體內容正在返回的路上
>4:DONE 響應主體內容已經返回到客戶端
#### HTTP網絡狀態碼(xhr.status)
記錄了當前服務器返回信息的狀態的狀態 通過`xhr.status`獲取


302 和 503(相對于302是沒有服務器幫忙)
#### 其它方法
```
readyState:存儲的是當前AJAX的狀態碼
```
如果請求的數據過大可能會觸發多次`readyState=3`的`onreadystatechange`回調

```
response/responseText/responseXML:都是用來接收服務器返回的響應主體中的內容,只是根據服務器返回的格式不一樣,我們使用撥通的屬性來接收即可
responseText是最常用的,接收到的結果是字符串格式(一般服務器返回的數據都是JSON格式字符串)
responseXML偶爾會用到,如果服務器端返回的是XML文檔數據,我們需要使用這個屬性接收
```
```
status:記錄了服務器端返回的HTTP狀態碼
statusText:對返回狀態碼的描述
```
```
timeout:設置當前AJAX請求的超時時間,假設我們設置時間為3000(MS),從AJAX請求發送開始,3秒后響應主體內容還沒有返回,瀏覽器會把當前AJAX請求任務強制斷開
```
```
upload
```
onload 只是`readyState=4`時會觸發

__proto__ 上的屬性和方法
七個常用的
```
abort():強制中斷AJAX請求
getAllResponseHeaders():獲取全部的響應頭信息(獲取的結果是一堆字符串文本)
getResponseHeader(key):獲取指定屬性名的響應頭信息,例如
xhr.getResponseHeader('date')獲取響應頭中存儲的服務器時間
open():打開url地址
overrideMimeType():重寫數據的MIME類型
send():發送AJAX請求(括號中書寫的內容是客戶端基于請求主體把信息傳遞給服務器)
setRequestHeader(key,value):設置請求頭信息(可以是設置的自定義請求頭信息)
```
事件

```
onabort:當AJAX被中斷請求時觸發
onreadystatechange:AJAX請求狀態改變會觸發這個事件
ontimeout:當AJAX請求超時,會觸發這個事件
```
## 編碼和解碼
設置請求頭的時候不能將內容設置成漢字
JS中常用的編碼解碼方法
### 正常的編碼解碼(非加密)
>escape/unescape:主要就是把中文漢字進行編碼和解碼 的(一般只有JS語言支持,一般經常用于前端頁面通信的時候的中文漢字編碼)
```
escape(str); //會將中文字符和空格進行編碼,其他不變
unescape("...."); //解碼
```
>encodeURI/decodeURI:基本上所有的編程語言都支持
```
encodeURI(str);
decodeURI("...");
```
>encodeURIComponent/decodeURIComponent
和第二種方式非常類似,區別在于

>需求:我們URL問號傳遞參數的時候,我們傳遞的參數值還是一個URL或則包含很多特殊的字符,此時為了不影響主要的URL,我們需要把傳遞的參數值進行編碼。使用`encodeURI`不能編碼一些特殊字符,所以只能使用`encodeURIComponent`處理
```
let str = 'http://www.baidu.com/?'
,obj = {
name:'ahhh'
,age:111
,url:'http://www.ahhh.com/?lx=1'
};
//=>把OBJ中的每一項屬性名和屬性值拼接到URL的末尾*(問號傳參方式)
for(let key in obj){
str += `${key}=${encodeURIComponent(obj[key])}&`;
}
//不能使用encodeURI必須使用encodeURIComponent,原因是encodeURI不能編碼特殊的字符
console.log(str.replace(/&$/g,''));
```

### 通過加密的方法進行編碼解碼
1、可逆轉加密(一般都是團隊自己玩的規則)
2、不可逆轉加密(一般都是基于MD5加密完成的,可能會把MD5加密后的結果二次加密)

## AJAX的同步和異步
### 同步

當調用`xhr.send()`時,才真正開始ajax請求,狀態才開始轉變。
```
AJAX這個任務:發送請求接收到響應體主體內容(完成一個完整的HTTP事務)
xhr.send(); 任務開始
xhr.readyState === 4; 任務結束
```
```
let xhr = new XMLHttpRequest();
xhr.open("get","temp.json",false);
xhr.onreadystatechange = ()=>{
console.log(xhr.readystate);
};
xhr.send();
//->只輸出一次 結果是4
```
同步的時候,以上代碼結果為`只輸出一次 結果是4`
```
let xhr = new XMLHttpRequest();
xhr.open("get","temp.json",false);
xhr.send(); //->【同步】,開始發送AJAX請求,開啟AJAX任務,在任務沒有完成之前什么事情都做不了->LOADING->當readyState===4的時候AJAX任務完成,開始執行下面操作
xhr.onreadystatechange = ()=>{
console.log(xhr.readystate);
};
//=>綁定方法之前狀態已經為4了,此時AJAX的狀態不會在改變成其它值了,所以時間永遠不會被觸發,一次都不執行
//SO,不要將send放在事件監聽前
```
### 異步
```
let xhr = new XMLHttpRequest();
xhr.open("get","temp.json");
xhr.onreadystatechange = function(){
console.log(xhr.readystate);
}
xhr.send();
```

```
let xhr = new XMLHttpRequest();
xhr.open("get","temp.json");
xhr.onreadystatechange = function(){
console.log(xhr.readystate);
}
xhr.send();
```
會輸出2、3、4
```
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
console.log(xhr.readystate);
}
xhr.open("get","temp.json");
xhr.send();
```
會輸出1、2、3、4
AJAX特殊的一點,執行open狀態變為1,會主動把之前監聽的方法執行一次,然后再執行SEND
```
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
console.log(xhr.readystate);
}
xhr.open("get","temp.json",false);
xhr.send();
```
會輸出1、4
## jqueryのajax
## 封裝原生ajax
```
封裝屬于自己的AJAX類庫
【支持的參數】
- url
- method/type
- data
- dataType
- cache
- async
- success
```
```
~function(){
class ajaxClass{
}
window.ajax = function({url=null,method='GET',type=null,data=null,dataType='JSON',cache=true,async=true,success=null}={}){
let example = new ajaxClass();
example.url = url;
example.method = type === null?method:type;
...
}'
}();
```

以上可以這么“優化”

ajaxClass

處理不同的mime 文件返回類型



## 關于post和get

`send()`調用后會真正開始ajax請求,如果傳了參這說明是post請求,發送的數據會放在請求報文里的`Request Payload`中

注意光以上這樣請求是會報錯的,

當我們想給服務器傳遞數據數據時,還需要告訴服務器我們傳送的數據是怎樣的形式

## 關于上傳和下載

### 上傳

html結構

```
btn.onclick = function(){
var fileInput = document.getELementById("fileInput");
var xhr = new XMLHttpRequest();
var progress = document.getELementById("progress");
var progress_num = document.getElementById("progress_num");
//post方式上傳
xhr.open("POST","地址",true);
//上傳文件進度監控
xhr.upload.onprogress = function(ev){
// console.log(ev);
var t= ev.loaded/ev.total;
var speed = Math.round(*100)+"%";
progress.style.width = t*500+'px';
progress_num.innerHTML = speed;
}
xhr.onload = function(){
console.log(xhr.status);
console.log(xhr.responseText);
}
console.log(fileInput.value); //是文件路徑,不是文件本身
console.dir(fileInput.files[0]);
//上傳文件上傳的是文件的二進制
//文件轉成二進制
var f = new FormData();
f.append('file',fileInput.files[0]); //key值 --- 文件
xhr.send(f);
}
```


上傳文件時必須轉換為二進制的形式,否則會導致如下結果

當調用`FormData`進行轉換后

### 如何監控上傳進度


### 上傳限制
后端會有限制

post_max_size : 一次post總共允許傳的大小(不僅包括上傳文件大小還能包括其他雜七雜八的表單數據)
upload_max_filesize : 上傳文件的大小
### 下載
```
<a href="E:\xxx.zip">xxx.zip</a>
```

點擊后瀏覽器會自動幫我們下載
- 空白目錄
- window
- location
- history
- DOM
- 什么是DOM
- JS盒子模型
- 13個核心屬性
- DOM優化
- 回流與重繪
- 未整理
- 文檔碎片
- DOM映射機制
- DOM庫封裝
- 事件
- 功能組件
- table
- 圖片延遲加載
- 跑馬燈
- 回到頂部
- 選項卡
- 鼠標跟隨
- 放大鏡
- 搜索
- 多級菜單
- 拖拽
- 瀑布流
- 數據類型的核心操作原理
- 變量提升
- 閉包(scope)
- this
- 練習題
- 各種數據類型下的常用方法
- JSON
- 數組
- object
- oop
- 單例模式
- 高級單例模式
- JS中常用的內置類
- 基于面向對象創建數據值
- 原型和原型鏈
- 可枚舉和不可枚舉
- Object.create
- 繼承的六種方式
- ES6下一代js標準
- babel
- 箭頭函數
- 對象
- es6勉強筆記
- 流程控制
- switch
- Ajax
- eval和()括號表達式
- 異常信息捕獲
- 邏輯與和或以及前后自增
- JS中的異步編程思想
- 上云
- 優化技巧
- 跨域與JSONP
- 其它跨域相關問題
- console
- HTML、XHTML、XML
- jQuery
- zepto
- 方法重寫和方法重載
- 移動端
- 響應式布局開發基礎
- 項目一:創意簡歷