其实际上和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 ); } } }; } });
默认值:jQuery智能猜测,猜测范围(xml、 json、 script或html)指定返回的数据类型。该属性值可以为:
cache
参数。注意:在远程请求时(不在同一个域下),所有POST请求都将转为GET请求。(因为将使用DOM的script标签来加载)。注意:如果是跨域的情况下,处理script的数据类型就会按照上面的ajaxTransport函数。否则还是通过通用的ajaxTransport返回"string"。null
或{}
。设置该请求加载的脚本文件的字符集。只有当请求时dataType为"jsonp"或"script",并且type是"GET"才会用于强制修改charset。这相当于设置<script>标签的charset属性。通常只在当前页面和远程数据的内容编码不同时使用。
默认值: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 ); } } }; } });
(1)CrossDomain
默认值:同域请求为false
,跨域请求为true
。指示是否是跨域请求。如果你想在同一域中强制跨域请求(如JSONP形式),请设置为true。例如,这允许服务器端重定向到另一个域。其实ajax函数内部会自动判断是否是跨域,如果是跨域那么就会把它设置为true,通过比较协议名称,域名,端口号来完成!
(1)open方法打开socket流,同时为XHR对象添加HTTP头部信息
(2)overrideMimeType方法,该方法的调用要在send方法之前才能保证修改mimeType!迫使XHR对象把相应当作特定格式处理!
(3)post请求通过send方法发送数据,get请求可以在send方法中也可以在URL后面!
(3)get,head请求不同
get,head请求是没有数据的,参数可以直接在URL里面,所以直接发送null,对于其它的请求必须把数据放在send方法里面发送出去!