XHR原生对象到底怎么仿AJAX?

XHR(XMLHttpRequest)

MDN文档

XHR是浏览器提供的能与服务端进行信息交互的对象,支持多种协议。

此接口继承了 XMLHttpRequestEventTarget 和 EventTarget 的属性。

属性

MLHttpRequest.onreadystatechange

  • 当readyState属性发生变化时调用的EventHandler。

XMLHttpRequest.readyState 只读

  • 返回 一个unsigned short 即无符号短整型,请求的状态码。

XMLHttpRequest.response 只读

  • 返回ArrayBuffer、Blob、Document、DOMString},具体是哪种类型取决于
    XMLHttpRequest.responseType的值。其中包含响应体body。

XMLHttpRequest.responseText 只读

  • 返回一个DOMString},该DOMString}包含对请求的响应,如果请求未成功或尚未发送,则返回null。

XMLHttpRequest.responseType

  • 定义响应类型的枚举值。

XMLHttpRequest.responseURL 只读

  • 返回响应的序列化URL,如果URL为空,则返回空字符串。

XMLHttpRequest.responseXML 只读 Not available to workers

  • 返回一个Document,其中包含该请求的响应,如果请求未成功、尚未发送或不能解析为XML或HTML,则返回null。

XMLHttpRequest.status 只读

  • 返回 unsigned short 即无符号短整型请求响应状态。

XMLHttpRequest.statusText 只读

  • 返回一个DOMString},其中包含HTTP服务器返回的响应状态。与 XMLHTTPRequest.status不同的是,它包括响应状态的整个文本(例如,“200 OK”)。

注意:根据HTTP/2规范(8.1.2.4响应伪标头字段),HTTP/2没有定义一种方式来携带HTTP/1.1状态行中包含的版本或原因短语。

XMLHttpRequest.timeout

  • unsigned long 即无符号长整型,表示该请求的最大请求时间(毫秒),超过该时间请求会自动结束。

XMLHttpRequestEventTarget.ontimeout

  • 当请求超时调用的EventHandler。{ { gecko_minversion_inline(“ 12.0 “)} }

XMLHttpRequest.upload 只读

  • XMLHttpRequestUpload,表示上传过程。

XMLHttpRequest.withCredentials

  • Boolean,用来指定跨域的请求是否应该使用证书(如cookie或授权header头)。

以下为非标准

XMLHttpRequest.channel只读

  • nsIChannel,对象在执行请求时使用的通道。

XMLHttpRequest.mozAnon只读

  • 一个布尔值,如果为真,请求将在没有cookie和身份验证header头的情况下发送。

XMLHttpRequest.mozSystem只读

  • 一个布尔值,如果为真,则在请求时不会强制执行同源策略。

XMLHttpRequest.mozBackgroundRequest

  • 一个布尔值,它指示对象是否是后台服务器端的请求

XMLHttpRequest.mozResponseArrayBuffer 已废弃 Gecko 6 只读

  • 一个ArrayBuffer类型,把请求的响应作为一个TypedArrays。

XMLHttpRequest.multipart已废弃 Gecko 22

  • 这个Gecko的独有属性,是一个布尔值,在Firefox/Gecko 22中被删除了。请使用Server-Sent Events, Web Sockets, 或来自进度事件的responseText代替

方法

XMLHttpRequest.abort()

  • 如果请求已经被发送,则立刻中止请求.

XMLHttpRequest.getAllResponseHeaders()

  • 以字符串的形式返回所有用CRLF分隔的响应头,如果没有收到响应,则返回null。

XMLHttpRequest.getResponseHeader()

  • 返回包含指定响应头的字符串,如果响应尚未收到或响应中不存在该报头,则返回null。

XMLHttpRequest.open()

  • 初始化一个请求。该方法只能JavaScript代码中使用,若要在native code中初始化请求,请使用openRequest()。

XMLHttpRequest.overrideMimeType()

  • 重写由服务器返回的MIME type。

XMLHttpRequest.send()

  • 发送请求。如果请求是异步的(默认),那么该方法将在请求发送后立即返回。

XMLHttpRequest.setRequestHeader()

  • 设置HTTP请求头的值。您必须在open()之后、send()之前调用setRequestHeader()这个方法。

说完属性和方法,到底怎么封装?

一般情况下,我们都会面向对象的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var ajax = (function() {
if (typeof XMLHttpRequest !== 'undefined') {
return new XMLHttpRequest();
} else if (typeof ActiveXObject !== 'undefined') {
if (typeof arguments.callee.activeXString !== 'string') {
var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
for (var i = 0,len = versions.length;i < len;i++) {
try {
var xhr = new ActiveXOject(versions[i]);
arguments.callee.activeXString = versions[i];
return xhr;
} catch (ex) {
continue;
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("no xhr object available");
}
}());

上面代码其实就是获取一个大当前浏览器支持的XHR对象,如果是现代浏览器那直接使用XMLHttpRequest对象,否则就是IE系列

接着就是基于XHR对象封装对应方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
var AJAX = function(ops) {
var root = this;
var req = ajax; //上面生成的ajax对象
root.url = ops.url;
root.type = ops.type || 'responseText';
root.contentType = ops.contentType || 'application/x-www-form-urlencoded';
root.method = ops.method || 'GET';
root.async = ops.async || true;
root.data = ops.data || {};
root.complete = ops.complete || function() {};
root.success = ops.success || function(){};
root.error = ops.error || function (s) { alert(root.url+'->status:'+s+'error!')};
root.abort = req.abort;
root.timeout = ops.timeout;
root.setData = function (data) {
for(var d in data) {
root.data[d] = data[d];
}
}
root.send = function () {
var datastring = root.contentType === 'application/x-www-form-urlencoded'? formatData(root.data) : root.data,
sendstring,get = false,
async = root.async,
complete = root.complete,
method = root.method,
type = root.type,
contentType = root.contentType;
if(method === 'GET') {
root.url += '?'+datastring;
get = true;
}
req.open(method, root.url, async);
if(!get) {
req.setRequestHeader("Content-type",contentType);
sendstring = datastring;
}
//在send之前重置onreadystatechange方法,否则会出现新的同步请求会执行两次成功回调(chrome等在同步请求时也会执行onreadystatechange)
req.onreadystatechange = async ? function () {
if (req.readyState == 4){
complete();
if(req.status == 200) {
root.success(req[type]);
} else {
root.error(req.status);
}
}
} : null;
req.send(sendstring);
if (!async) {
complete();
root.success(req[type]);
}
}
root.url && root.send();
};

最后来一手正儿八经的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
;(function (name, root, factory) {
var module = factory(root);
if (typeof define === 'function' && define.amd) { //require AMD
define(function () {
return module;
});
} else if (typeof exports === 'object') { //common CMD
module.exports = module;
} else {
root[name] = module;
}
window[name] = module
})('_ajax', this, function(){
var xhr = function() {
var ajax = (function() {
if (typeof XMLHttpRequest !== 'undefined') {
return new XMLHttpRequest();
} else if (typeof ActiveXObject !== 'undefined') {
if (typeof arguments.callee.activeXString !== 'string') {
var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
for (var i = 0,len = versions.length;i < len;i++) {
try {
var xhr = new ActiveXOject(versions[i]);
arguments.callee.activeXString = versions[i];
return xhr;
} catch (ex) {
continue;
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("no xhr object available");
}
}());
var formatData = function(fd) {
var res = '';
for (var f in fd) {
res += f+'='+fd[f]+'&';
}
return res.slice(0, -1);
};
var AJAX = function(ops) {
var root = this;
var req = ajax;
root.url = ops.url;
root.type = ops.type || 'responseText';
root.contentType = ops.contentType || 'application/x-www-form-urlencoded';
root.method = ops.method || 'GET';
root.async = ops.async || true;
root.data = ops.data || {};
root.complete = ops.complete || function() {};
root.success = ops.success || function(){};
root.error = ops.error || function (s) { alert(root.url+'->status:'+s+'error!')};
root.abort = req.abort;
root.timeout = ops.timeout;
root.setData = function (data) {
for(var d in data) {
root.data[d] = data[d];
}
}
root.send = function () {
var datastring = root.contentType==='application/x-www-form-urlencoded'? formatData(root.data) : root.data,
sendstring,get = false,
async = root.async,
complete = root.complete,
method = root.method,
type = root.type,
contentType = root.contentType;
if(method === 'GET') {
root.url += '?'+datastring;
get = true;
}
req.open(method, root.url, async);
if(!get) {
req.setRequestHeader("Content-type",contentType);
sendstring = datastring;
}
//在send之前重置onreadystatechange方法,否则会出现新的同步请求会执行两次成功回调(chrome等在同步请求时也会执行onreadystatechange)
req.onreadystatechange = async ? function () {
// console.log('async true');
if (req.readyState ==4){
complete();
if(req.status == 200) {
root.success(req[type]);
} else {
root.error(req.status);
}
}
} : null;
req.send(sendstring);
if(!async) {
//console.log('async false');
complete();
root.success(req[type]);
}
}
root.url && root.send();
};
return function(ops) {return new AJAX(ops);}
}();
return xhr
})