您的位置:首页 > Web前端 > JQuery

jQuery源码分析之ajaxTransport

2015-11-02 15:39 851 查看
其实际上和ajaxPrefilters逻辑一样,还是通过返回一个闭包函数来完成的。

function addToPrefiltersOrTransports( structure ) {
// dataTypeExpression is optional and defaults to "*"
return function( dataTypeExpression, func ) {
if ( typeof dataTypeExpression !== "string" ) {
func = dataTypeExpression;
dataTypeExpression = "*";
}
var dataType,
i = 0,
dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
if ( jQuery.isFunction( func ) ) {
// For each dataType in the dataTypeExpression
while ( (dataType = dataTypes[i++]) ) {
// Prepend if requested
if ( dataType.charAt( 0 ) === "+" ) {
dataType = dataType.slice( 1 ) || "*";
(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
// Otherwise append
} else {
(structure[ dataType ] = structure[ dataType ] || []).push( func );
}
}
}
};
}
ajaxTransport源码:
ajaxTransport: addToPrefiltersOrTransports( transports )

ajaxTransport其实是通过闭包来完成的,简洁版的如下:

function addToPrefiltersOrTransports(structure)//闭包不会释放对structure的引用,所以一直在内存中!
{
return function(dataTypeExpression,func)
{
structure[dataTypeExpression]=structure[dataTypeExpression].push(func);	//一直引用外部参数!
}
}
var prefilters={};//用于被外部函数引用,从而一直在内存中
function f1()
{
alert("f1");
}
function f2()
{
alert("f2");
}
var result=addToPrefiltersOrTransports(prefilters);
result("json",f1);
result("json",f2);
prefilters["json"][0]();//打印f1
prefilters["json"][1]();//打印f2


当然,简洁版的不满足在同一类型的回调函数的头部添加,如果要在回调函数的头部添加可以通过在前面添加加号来完成

var func1=function(){alert(2);}
var func=function(){alert(1);}
var prefilters={};
//返回匿名函数
var resultFunc=addToPrefiltersOrTransports(prefilters);
//这时候prefilters里面已经被添加了func函数了
//所以prefilters["jsonp"]=[func],prefilters["html"]=[func]
resultFunc("jsonp html",func);
resultFunc("+jsonp +html",func1);
alert(prefilters["jsonp"]);
//打印数组[func1,func]也就是如果在前面添加一个加号,那么就会加入到prefilters或者transport的最前面,这是unshift和push的区别
//push表示在后面添加。如果上面没有加号,那么打印就是[func,func1]
note:通过在调用返回的闭包函数添加的类型前面添加加号,可以在回调类型集合的前面添加回调,因为特定类型的回调函数在prefilters或者transpot中都是集合!注意,如果调用闭包函数没有传入第一个参数,那么就会添加到以*为键名的集合中,而不是添加到如prefilters["json"],而是prefilters["*"]。

ajaxTransport对script标签的特殊处理:

jQuery.ajaxTransport( "script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script,
head = document.head || jQuery("head")[0] || document.documentElement;
return {
send: function( _, callback ) {
script = document.createElement("script");
script.async = true;
if ( s.scriptCharset ) {//只有dataType是"script","jsonp"并且type是get时候才会修改!
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {
if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if ( script.parentNode ) {
script.parentNode.removeChild( script );
}
// Dereference the script
script = null;
// Callback if not abort
if ( !isAbort ) {
callback( 200, "success" );
}
}
};
// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
// Use native DOM manipulation to avoid our domManip AJAX trickery
head.insertBefore( script, head.firstChild );
},
abort: function() {
if ( script ) {
script.onload( undefined, true );
}
}
};
}
});



dataTypeString类型

默认值:jQuery智能猜测,猜测范围(xml
json、 script或html)指定返回的数据类型。该属性值可以为:

'xml'
:返回XML文档,可使用jQuery进行处理。

'html': 返回HTML字符串。

'script': 返回JavaScript代码。不会自动缓存结果。除非设置了
cache
参数。注意:在远程请求时(不在同一个域下),所有POST请求都将转为GET请求。(因为将使用DOM的script标签来加载)。注意:如果是跨域的情况下,处理script的数据类型就会按照上面的ajaxTransport函数。否则还是通过通用的ajaxTransport返回"string"。

'json': 返回JSON数据。JSON数据将使用严格的语法进行解析(属性名必须加双引号,所有字符串也必须用双引号),如果解析失败将抛出一个错误。从jQuery
1.9开始,空内容的响应将返回
null
{}


'jsonp': JSONP格式。使用JSONP形式调用函数时,如"url?callback=?",jQuery将自动替换第二个?为正确的函数名,以执行回调函数。

'text': 返回纯文本字符串。


scriptCharsetString类型

设置该请求加载的脚本文件的字符集。只有当请求时dataType为"jsonp"或"script",并且type是"GET"才会用于强制修改charset。这相当于设置<script>标签的charset属性。通常只在当前页面和远程数据的内容编码不同时使用。


asyncBoolean类型

默认值:
true


指示是否是异步请求。同步请求将锁定浏览器,直到获取到远程数据后才能执行其他操作。
note:该ajaxTransport函数只是为了处理跨域的请求,而且处理的是script标签的跨域请求!他内部的逻辑还是很简单的,只是创建一个script标签,同时async设为true表示异步的,同时监听onload和onreadyStateChange事件!

如果浏览器能够返回XHR对象那么添加一个ajaxTransport:

var xhrSupported = jQuery.ajaxSettings.xhr();//返回一个XHR对象
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
xhrSupported = support.ajax = !!xhrSupported;
//他调用上面的ajaxTransport方法返回的对象,并且传入一个函数!那么这个函数被放到transports对象中!
jQuery.ajaxTransport(function( options ) {
// Cross domain only allowed if supported through XMLHttpRequest
//如果没有执行跨域,但是支持cors
if ( !options.crossDomain || support.cors ) {
var callback;
return {
//send方法
send: function( headers, complete ) {
var i,
//获取xhr对象
xhr = options.xhr(),
//保存xhr的数量
id = ++xhrId;
//打开socket!
// Open the socket
xhr.open( options.type, options.url, options.async, options.username, options.password );

// Apply custom fields if provided
//如果options提供了xhrFields,如options.xhrFields={name:"xxx",sex:"female"}
//最终就是xhr["name"]="xxx",xhr["sex"]="female"
if ( options.xhrFields ) {
for ( i in options.xhrFields ) {
xhr[ i ] = options.xhrFields[ i ];
}
}
//如果提供了mimeType那么覆盖本身的mimeType类型!
// Override mime type if needed
if ( options.mimeType && xhr.overrideMimeType ) {
xhr.overrideMimeType( options.mimeType );
}
// X-Requested-With header
// For cross-domain requests, seeing as conditions for a preflight are
// akin to a jigsaw puzzle, we simply never set it to be sure.
// (it can always be set on a per-request basis or even using ajaxSetup)
// For same-domain requests, won't change header if already provided.
//如果没有提供crossDomain同时header里面也没有X-Requested-With
//那么headers["X-Requested-With"]就是XMLHttpRequest
// X-Requested-With请求头用于在服务器端判断request来自Ajax请求还是传统请求。
 				   // 两种请求在请求的Header不同,Ajax 异步请求比传统的同步请求多了一个头参数
//但是要设置 X-Requested-With头要满足双重条件才可:
//  (1)没有crossDomain
//  (2)X-Requested-With不存在!如果存在就不要这里设置,因为已经在headers里面了!

if ( !options.crossDomain && !headers["X-Requested-With"] ) {
headers["X-Requested-With"] = "XMLHttpRequest";
}
// Set headers
for ( i in headers ) {
// Support: IE<9
// IE's ActiveXObject throws a 'Type Mismatch' exception when setting
// request header to a null-value.
//
// To keep consistent with other XHR implementations, cast the value
// to string and ignore `undefined`.
if ( headers[ i ] !== undefined ) {
xhr.setRequestHeader( i, headers[ i ] + "" );
}
}
// Do send the request
// This may raise an exception which is actually
// handled in jQuery.ajax (so no try/catch here)
//	s.hasContent = !rnoContent.test( s.type );
//rnoContent = /^(?:GET|HEAD)$/,
//get和head请求没有数据
//这里就是说:如果不是get,head请求同时options有data那么必须要在send方法传出去
//如果是get,head请求那么直接传送null!
xhr.send( ( options.hasContent && options.data ) || null );
// Listener
callback = function( _, isAbort ) {
var status, statusText, responses;
//如果callbacks存在,同时isAbort为false表示没有放弃执行
// Was never called and is aborted or complete
if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
// Clean up
//清除回调!同时从xhrCallbacks中清除!
delete xhrCallbacks[ id ];
callback = undefined;
//将事件变化函数绑定为空函数
xhr.onreadystatechange = jQuery.noop;
//如果要移除,而且readyState不是4,那么手动调用abort方法
// Abort manually if needed
if ( isAbort ) {
if ( xhr.readyState !== 4 ) {
xhr.abort();
}
} else {
//如果不是调用abort,那么这时候isAbort就是undefined!
responses = {};
status = xhr.status;
// Support: IE<10
// Accessing binary-data responseText throws an exception
// (#11426)
//如果responseText是string那么responses的text就是responseText
//IE<10用responseText获取二进制数据时候报错!
if ( typeof xhr.responseText === "string" ) {
responses.text = xhr.responseText;
}
//火狐在用statusText处理跨域请求时候报错!
// Firefox throws an exception when accessing
// statusText for faulty cross-domain requests
try {
statusText = xhr.statusText;
} catch( e ) {
// We normalize with Webkit giving an empty statusText
statusText = "";
}
// Filter status for non standard behaviors
// If the request is local and we have data: assume a success
// (success with no data won't get notified, that's the best we
// can do given current implementations)
//允许将当前环境视作"本地",(例如文件系统),即使默认情况下jQuery不会如此识别它。
//目前,以下协议将被视作本地:file、*-extension和widget。
//如果status不存在,同时isLocal是true,同时crossDomain是false
//那么如果responses.text存在那么status就是200,否则就是404!
if ( !status && options.isLocal && !options.crossDomain ) {
status = responses.text ? 200 : 404;
// IE - #1450: sometimes returns 1223 when it should be 204
//IE下有时候返回1123,但是应该是204!
} else if ( status === 1223 ) {
status = 204;
}
}
}
// Call complete if needed
if ( responses ) {
complete( status, statusText, responses, xhr.getAllResponseHeaders() );
}
};
//如果没有指定async那么直接调用callback方法!
if ( !options.async ) {
// if we're in sync mode we fire the callback
callback();
} else if ( xhr.readyState === 4 ) {
//在IE6,7中,readyState是4的时候可能是读了缓存,这种情况下要手动调用回调函数!
// (IE6 & IE7) if it's in cache and has been
// retrieved directly we need to fire the callback
setTimeout( callback );
} else {
// Add to the list of active xhr callbacks

//var xhrId = 0,
//xhrCallbacks = {}
//xhrCallbacks[i]=callback说明这个对象是为了保存回调函数的!
xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
}
},
//如果调用abort方法,那么回调的时候第一个参数是undefined,第二个参数是true
abort: function() {
if ( callback ) {
callback( undefined, true );
}
}
};
}
});


note:

(1)CrossDomain

默认值:同域请求为
false
,跨域请求为
true
。指示是否是跨域请求。如果你想在同一域中强制跨域请求(如JSONP形式),请设置为true。例如,这允许服务器端重定向到另一个域。其实ajax函数内部会自动判断是否是跨域,如果是跨域那么就会把它设置为true,通过比较协议名称,域名,端口号来完成!
(2)调用步骤
(1)open方法打开socket流,同时为XHR对象添加HTTP头部信息

(2)overrideMimeType方法,该方法的调用要在send方法之前才能保证修改mimeType!迫使XHR对象把相应当作特定格式处理!

(3)post请求通过send方法发送数据,get请求可以在send方法中也可以在URL后面!

(3)get,head请求不同

get,head请求是没有数据的,参数可以直接在URL里面,所以直接发送null,对于其它的请求必须把数据放在send方法里面发送出去!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: