上一篇文章 ,将jQuery.ajax中的一些细节补充完。这篇文章讲解如果将类AJAX方法都包装进jQuery.ajax中。下篇文章则讲解各预过滤器和分发器的细节。
为什么要包装起来?
我们知道,古老的XMLHttpRequest出于同源策略考虑,是不支持跨域的。所以,在前端想动态加载跨域Javascript脚本,通常是使用被称为Script DOM Element的方案,如:
var
scriptElem = document.createElement("script"
);
scriptElem.src
= "http://anydomain.com/A.js"
;
document.getElementsByTagName(
"head")[0].appendChild(scriptElem);
同理,JSON也无法通过XMLHttpRequest进行跨域,所以利用Script DOM Element,将JSON填入一个回调函数中来实现其跨域,也就是JSONP(JSON with padding, 填充式JSON或参数式JSON)。
实际上JSONP就是将,得到JSON后回调的函数通过GET传参告诉服务器,然后服务器拼接一段脚本,用该回调函数并参数为需要的JSON数据,如:
callback({ "name": "Justany_WhiteSnow" });
jQuery团队当然希望开发者开发的时候,不需要想需不需要跨域,只要直接使用就行了。
所以他们把XMLHttpRequest、Script DOM Element、JSONP包装起来,都当成AJAX来使用。
这里顺便提一下,其实现代浏览器(Firefox 3.5+、Safari 4+、Chrome等)中,通过XMLHttpRequeest实现了CORS(Cross-Origin Resource Sharing, 跨源资源共享)原生支持。也就是XMLHttpRequest在某些浏览器中,实际上是可以跨域的,只需要设置一下HTTP Response Header中的Access-Control-Allow-Origin。比如设置成通配符*。
而IE8也引入XDomainRequest也实现了CORS。
但毕竟某些浏览器不行,所以,咳咳……这不能成为一种通用方案。
怎么包装起来?
首先我们有一个山寨XHR对象,也就是jqXHR对象。通过对其添加 send、 abort来模拟XHR对象。
可是我们需要在不同方案执行前先处理一下特异性的东东,所以我们需要一个预过滤机制( Prefilter )来预先处理一下。
然后我们需要知道到底应当用那一套方案来执行整个过程,所以我们需要一个分发机制( Transport )来得到最后的jqXHR对象。
inspectPrefiltersOrTransports
我们在jQuery.ajax找到了预过滤和分发机制的函数,inspectPrefiltersOrTransports。
//
预过滤
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
//
……
//
得到transport
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
然后我们看看这个函数在干些什么。
//
检测函数,预过滤或者分发器
function
inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
//
定义个参数对象
var
inspected =
{},
//
看看传进来的结构体是prefilters, 还是transports
//
如果是prefilters,证明这是预过滤过程,如果是transports分发过程
//
所以这个是,是不是在分发过程
seekingTransport = ( structure ===
transports );
function
inspect( dataType ) {
var
selected;
//
将inspected的dataType对应属性设置成true
inspected[ dataType ] =
true
;
//
遍历prefilters对应dataType对象中的所有过滤器或者转换器工厂函数
jQuery.each( structure[ dataType ] || [],
function
( _, prefilterOrFactory ) {
//
得到dataType或者转换器
var
dataTypeOrTransport =
prefilterOrFactory( options, originalOptions, jqXHR );
//
如果dataType为字符串,即上面过程是过滤器
//
如果在预过滤过程
//
并且这个过滤出来的dataType不等于刚开始传进来的dataType
if
(
typeof
dataTypeOrTransport === "string" && !seekingTransport && !
inspected[ dataTypeOrTransport ] ) {
//
将现在这个新的dataType插入到options中
options.dataTypes.unshift( dataTypeOrTransport );
//
检测新的dataType
inspect( dataTypeOrTransport );
return
false
;
//
否则如果在分发过程
}
else
if
( seekingTransport ) {
//
定义selected为dataTypeOrTransport
return
!( selected =
dataTypeOrTransport );
}
});
return
selected;
}
//
检查dataTypes数组的第一个,如果结果是undefined,
//
则看看上面检查的是不是通配符*,如果不是则检查通配符
return
inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*"
);
}
我们可以看到,这个函数实际上就是从prefilters和transports中取出对应dataType的东东,然后过滤或者分发。
那么怎么定义prefilters和transports这两个对象的呢?
jQuery.ajaxPrefilter & jQuery.ajaxTransport
实际上,jQuery是通过这两个接口来定义上面两个对象的内容的,一个是定义预过滤器,另一个则定义分发器。
jQuery.ajaxPrefilter =
addToPrefiltersOrTransports( prefilters );
jQuery.ajaxTransport
= addToPrefiltersOrTransports( transports );
而这两个方法都是由addToPrefiltersOrTransports生成的。
addToPrefiltersOrTransports
//
jQuery.ajaxPrefilter和jQuery.ajaxTransport的构造函数
function
addToPrefiltersOrTransports( structure ) {
//
dataTypeExpression是可选参数,缺省值为*
return
function
( dataTypeExpression, func ) {
//
如果dataTypeExpression不是字符串
//
模拟重载
if
(
typeof
dataTypeExpression !== "string"
) {
func
=
dataTypeExpression;
//
缺省为*
dataTypeExpression = "*"
;
}
var
dataType,
i
= 0
,
//
将dataTypeExpression转为小写,并用空白拆成数组
dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) ||
[];
//
如果func是一个函数
if
( jQuery.isFunction( func ) ) {
//
遍历dataTypeExpression中的所有dataType
while
( (dataType = dataTypes[i++
]) ) {
//
如果有必要则删除头部的+号
if
( dataType[0] === "+"
) {
//
删除+号
dataType = dataType.slice( 1 ) || "*"
;
//
往structure对应的dataType中推入func函数
(structure[ dataType ] = structure[ dataType ] ||
[]).unshift( func );
//
否则不需要处理
}
else
{
//
往structure对应的dataType中推入func函数
(structure[ dataType ] = structure[ dataType ] ||
[]).push( func );
}
}
}
};
}
接下来就是通过调用jQuery.ajaxPrefilter和jQuery.ajaxTransport方法,添加预过滤器和分发器来完成包装。

