
目錄
- 沒有 AJAX 的年代,怎么發(fā)請求
- AJAX 是什么
- XMLHttpRequest 的實例屬性
- XMLHttpRequest 的實例方法
- 手寫 AJAX
- (1)原生 ajax
- (2)封裝 jQuery.ajax
- 總結(jié)
沒有 AJAX 的年代,怎么發(fā)請求
- 用
<form>可以發(fā)請求,但是會刷新頁面或新開頁面

- 用
<a>可以發(fā) get 請求,但是也會刷新頁面或新開頁面

- 用
<img>可以發(fā) get 請求,但是只能以圖片的形式展示

- 用
<link>可以發(fā) get 請求,但是只能以 CSS、favicon 的形式展示

- 用
<script>可以發(fā) get 請求,但是只能以腳本的形式運行

AJAX 是什么
AJAX(Asynchronous JavaScript and XML),指的是通過 JavaScript 的異步通信,從服務(wù)器獲取 XML 文檔從中提取數(shù)據(jù),再更新當前網(wǎng)頁的對應(yīng)部分,而不用刷新整個網(wǎng)頁。
后來,AJAX 這個詞就成為 JavaScript 腳本發(fā)起 HTTP 通信的代名詞,也就是說,只要用腳本發(fā)起通信,就可以叫做 AJAX 通信。
AJAX 的步驟
- 創(chuàng)建 XMLHttpRequest 實例
- 發(fā)出 HTTP 請求
- 服務(wù)器返回 XML 格式的字符串
- JS 解析 XML,并更新局部頁面
不過隨著歷史進程的推進,XML 已經(jīng)被淘汰,取而代之的是 JSON。
JSON(JavaScript Object Notation,JavaScript對象表示法)是一種由 Douglas Crockford 構(gòu)想和設(shè)計、輕量級的數(shù)據(jù)交換語言。它是 JavaScript 的一個子集,因此 JSON 在語法上保留了很多 JavaScript 的特征。
區(qū)別:
- JSON 沒有 function、undefined,也沒有 Number 中的 NaN 和 Infinity
- JSON 字符串的首尾必須是雙引號,這意味著對象的鍵也必須加上雙引號
- JSON 只是一種數(shù)據(jù)格式,數(shù)據(jù)格式其實就是一種規(guī)范,格式、形式、規(guī)范是不能用來存諸數(shù)據(jù)的。因此諸如
var obj={"width":100,"height":200,"name":"rose"}這樣的不能稱之為 JSON 對象,而是一種 JSON 格式的 JS 對象。
如下是 JavaScript 與 JSON 的對比:

XMLHttpRequest 對象是 AJAX 的主要接口,用于瀏覽器與服務(wù)器之間的通信。盡管名字里面有 XML 和 HTTP,它實際上可以使用多種協(xié)議(比如 file 或 ftp),發(fā)送任何格式的數(shù)據(jù)(包括字符串和二進制)。
注意:AJAX 只能向同源網(wǎng)址(協(xié)議、域名、端口都相同)發(fā)出 HTTP 請求,如果發(fā)出跨域請求,就會報錯。
XMLHttpRequest 的實例屬性
XMLHttpRequest.readyState
XMLHttpRequest.readyState 屬性返回一個 XMLHttpRequest 代理當前所處的狀態(tài)。
| 值 | 狀態(tài) | 描述 |
|---|---|---|
| 0 | UNSENT | 代理被創(chuàng)建,但尚未調(diào)用 open() 方法 |
| 1 | OPENED | open() 方法已經(jīng)被調(diào)用 |
| 2 | HEADERS_RECEIVED | send() 方法已經(jīng)被調(diào)用,并且頭部和狀態(tài)已經(jīng)可獲得 |
| 3 | LOADING | 下載中; responseText 屬性已經(jīng)包含部分數(shù)據(jù) |
| 4 | DONE | 下載操作已完成 |
XMLHttpRequest.status
XMLHttpRequest.status 屬性返回一個整數(shù),表示服務(wù)器回應(yīng)的 HTTP 狀態(tài)碼。
XMLHttpRequest.onreadystatechange
XMLHttpRequest.onreadystatechange 屬性指向一個監(jiān)聽函數(shù)。readystatechange 事件發(fā)生時(實例的 readyState 屬性變化),就會執(zhí)行這個屬性。
XMLHttpRequest.responseText
XMLHttpRequest.responseText 屬性返回從服務(wù)器接收到的字符串,該屬性為只讀。只有 HTTP 請求完成接收以后,該屬性才會包含完整的數(shù)據(jù)。
XMLHttpRequest 的實例方法
XMLHttpRequest.open()
XMLHttpRequest.open 方法用于指定 HTTP 請求的參數(shù),或者說初始化 XMLHttpRequest 實例對象。
它一共可以接受五個參數(shù):
- method:表示 HTTP 動詞方法,比如GET、POST、PUT、DELETE、HEAD等
- url: 表示請求發(fā)送目標 URL
- async(可選): 布爾值,表示請求是否為異步,默認為 true。如果設(shè)為 false,則 send() 方法只有等到收到服務(wù)器返回了結(jié)果,才會進行下一步操作。由于同步 AJAX 請求會造成瀏覽器失去響應(yīng),許多瀏覽器已經(jīng)禁止在主線程使用,只允許 Worker 里面使用。所以,這個參數(shù)輕易不應(yīng)該設(shè)為false。
- user(可選):表示用于認證的用戶名,默認為空字符串
- password(可選):表示用于認證的密碼,默認為空字符串
注意:再次使用 open(),等同于調(diào)用 abort()。
XMLHttpRequest.send()
XMLHttpRequest.send 方法用于實際發(fā)出 HTTP 請求。
它的參數(shù)是可選的:
- 如果不帶參數(shù),就表示 HTTP 請求只有一個 URL,沒有數(shù)據(jù)體,典型例子就是 GET 請求
- 如果帶有參數(shù),就表示除了頭信息,還帶有包含具體數(shù)據(jù)的信息體,典型例子就是 POST 請求
GET 請求:
var xhr = new XMLHttpRequest();
xhr.open('GET',
'http://www.example.com/?id=' + encodeURIComponent(id),
true
);
xhr.send(null);
POST 請求:
var xhr = new XMLHttpRequest();
var data = 'email='
+ encodeURIComponent(email)
+ '&password='
+ encodeURIComponent(password);
xhr.open('POST', 'http://www.example.com', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);
XMLHttpRequest.setRequestHeader()
XMLHttpRequest.setRequestHeader 方法用于設(shè)置瀏覽器發(fā)送的 HTTP 請求的頭信息。
該方法必須在open()之后、send()之前調(diào)用。
該方法接受兩個參數(shù)。第一個參數(shù)是字符串,表示頭信息的字段名,第二個參數(shù)是字段值。
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));
XMLHttpRequest.getResponseHeader()
XMLHttpRequest.getResponseHeader 方法返回 HTTP 頭信息指定字段的值。
XMLHttpRequest.getAllResponseHeaders()
XMLHttpRequest.getAllResponseHeaders 方法返回一個字符串,表示服務(wù)器發(fā)來的所有 HTTP 頭信息。
格式為字符串,每個頭信息之間使用 CRLF 分隔(回車+換行),如果沒有收到服務(wù)器回應(yīng),該屬性為 null。如果發(fā)生網(wǎng)絡(luò)錯誤,該屬性為空字符串。
手寫 AJAX
簡而言之,AJAX 就是在瀏覽器通過 XMLHttpRequest 對象, 構(gòu)造(set)HTTP 請求和獲?。╣et)HTTP 響應(yīng)的技術(shù)。
那么 AJAX 具體如何實現(xiàn)?
- JS 設(shè)置(set)任意請求 header
- 請求內(nèi)容第一部分
request.open('get', '/xxx') - 請求內(nèi)容第二部分
request.setRequestHeader('content-type','x-www-form-urlencoded') - 請求內(nèi)容第四部分
request.send('a=1&b=2')
- 請求內(nèi)容第一部分
- JS 獲?。╣et)任意響應(yīng) header
- 響應(yīng)內(nèi)容第一部分
request.status / request.statusText - 響應(yīng)內(nèi)容第二部分
request.getResponseHeader() / request.getAllResponseHeaders() - 響應(yīng)內(nèi)容第四部分
request.responseText
- 響應(yīng)內(nèi)容第一部分
(1)原生 ajax
了解了屬性和方法之后,根據(jù) AJAX 的步驟,手寫最簡單的 GET 請求。
version 1.0:
myButton.addEventListener('click', function(){
ajax()
})
function ajax() {
let request = new XMLHttpRequest()
request.open('get', 'https://www.google.com')
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status >= 200 && request.status <300) {
let string = request.responseText
let object = JSON.parse(string)
}
}
}
request.send()
}
在 1.0 版本中,并沒有設(shè)置請求報頭,因為是 GET 請求,所以 send() 中也沒有設(shè)置請求主體。并且,其內(nèi)容是寫死的,我們應(yīng)該從變量來獲取更好。
(2)封裝 jQuery.ajax
需求:實現(xiàn)一個滿足這些 API 的 jQuery.ajax(url, method, body, success, fail)。
在之前的文章中,我實現(xiàn)了一個簡易版 jQuery,這里就在它的基礎(chǔ)上再添加函數(shù) ajax。
version 2.0:
myButton.addEventListener("click", (e) => {
$.ajax(
'/xxx',
'post',
'a=1&b=2',
(responseText) => { console.log('success') },
(request) => { console.log('fail') }
)
})
window.jQuery = function(nodeOrSelector) {
let nodes = {}
return nodes
}
window.jQuery.ajax = function(url, method, body, success, fail) {
let request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status >= 200 && request.status <300) {
success.call(undefined, request.responseText)
} else if (request.status >= 400) {
fail.call(undefined, request)
}
}
}
request.send(body)
}
window.$ = window.jQuery
version 2.0 版本實現(xiàn)了 jQuery 版本的以變量傳入的方式實現(xiàn)API的 ajax,但是封裝得并不好,容易忘記每個參數(shù)是什么,并且諸如 GET 請求并不需要參數(shù) body,因此這個位置應(yīng)該填入 undefined 或者 null,這樣代碼就很丑。
因此,我們需要傳入一個有結(jié)構(gòu)的參數(shù)來包含上述功能。
version 3.0:
myButton.addEventListener("click", (e) => {
$.ajax({
url: '/xxx',
method: 'post',
body: 'a=1&b=2',
success: (responseText) => {
console.log('success')
console.log(responseText)
},
fail: (request) => {
console.log('fail')
console.log(request.status)
}
})
})
window.jQuery = function (nodeOrSelector) {
let nodes = {}
return nodes
}
window.jQuery.ajax = function (options) {
let url = options.url
let method = options.method
let body = options.body
let success = options.success
let fail = options.fail
let request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status >= 200 && request.status < 300) {
success.call(undefined, request.responseText)
} else if (request.status >= 400) {
fail.call(undefined, request)
}
}
}
request.send(body)
}
window.$ = window.jQuery
繼續(xù)優(yōu)化上面的代碼,帶入ES6中數(shù)組的解構(gòu)賦值。
// ES 5
let url = options.url
let method = options.method
let body = options.body
let success = options.success
let fail = options.fail
// ES 6
let {url, method, body, success, fail} = options
并且,由于 options 參數(shù)只使用了一次,那么可以直接省略掉。得到:
version 4.0:
myButton.addEventListener("click", (e) => {
$.ajax({
url: '/xxx',
method: 'post',
body: 'a=1&b=2',
success: (responseText) => {
console.log('success')
console.log(responseText)
},
fail: (request) => {
console.log('fail')
console.log(request.status)
}
})
})
window.jQuery = function (nodeOrSelector) {
let nodes = {}
return nodes
}
window.jQuery.ajax = function ({ url, method, body, success, fail }) {
let request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status >= 200 && request.status < 300) {
success.call(undefined, request.responseText)
} else if (request.status >= 400) {
fail.call(undefined, request)
}
}
}
request.send(body)
}
window.$ = window.jQuery
總結(jié)
AJAX 非常重要,基本上,有了 AJAX 之后,前端才被稱之為前端,在這之前的程序員,基本可以被稱為頁面仔。因此,深入理解 AJAX 的手動實現(xiàn),如何設(shè)置和獲取 request 和 response,完成 HTTP 請求,是學(xué)習的重點,也是面試常考的內(nèi)容。
這里只提到了比較淺顯的 HTTP 相關(guān)知識,在后面補看《HTTP權(quán)威指南》后,會對 AJAX 有更深入的理解。