ajaxSetup源码分析:
ajaxSetup: function( target, settings ) { return settings ? // Building a settings object ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : // Extending ajaxSettings ajaxExtend( jQuery.ajaxSettings, target ); }note:下面对于"text script"或者"text jsonp"这种类型我们只是传入了一个参数, 最后我们把这些属性全部封装到了jQuery.ajaxSettings方法上面!也就是我们最后的options对象!
对于“text script”的类型的处理:
jQuery.ajaxSetup({ accepts: { script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" }, contents: { script: /(?:java|ecma)script/ }, converters: { "text script": function( text ) { jQuery.globalEval( text ); return text; } } });note:直接调用了ajaxSetup方法,所以这个converters也会放入到最终的options中间,所以如果你在dataType里面传入"text script",那么就会按照这种方式来解析。也就是直接调用jQuery.globalEval函数, 最终如果用户的dataType是"text script"那么结果就是直接执行!
jQuery.ajaxSetup({ jsonp: "callback", jsonpCallback: function() { var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); this[ callback ] = true; return callback; } });对于json和jsonp数据都是经过预先处理的,也就是说如果我们在dataType中传入了json或者jsonp那么我们在发送ajax请求之前都是经过特殊处理的!
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { var callbackName, overwritten, responseContainer, jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? "url" : typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" ); // Handle iff the expected data type is "jsonp" or we have a parameter to set if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { // Get callback name, remembering preexisting value associated with it callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback; // Insert callback into url or form data if ( jsonProp ) { s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); } else if ( s.jsonp !== false ) { s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; } // Use data converter to retrieve json after script execution s.converters["script json"] = function() { if ( !responseContainer ) { jQuery.error( callbackName + " was not called" ); } return responseContainer[ 0 ]; }; // force json dataType s.dataTypes[ 0 ] = "json"; //如果是json或者jsonp请求,那么我们最后把dataTypes[0]修改为json,那么我们最后服务器返回数据的时候 //就可以在converter里面找到json是按照parseJSON方法来完成的! // Install callback overwritten = window[ callbackName ]; window[ callbackName ] = function() { responseContainer = arguments; }; // Clean-up function (fires after converters) jqXHR.always(function() { // Restore preexisting value window[ callbackName ] = overwritten; // Save back as free if ( s[ callbackName ] ) { // make sure that re-using the options doesn't screw things around s.jsonpCallback = originalSettings.jsonpCallback; // save the callback name for future use oldCallbacks.push( callbackName ); } // Call if it was a function and we have a response if ( responseContainer && jQuery.isFunction( overwritten ) ) { overwritten( responseContainer[ 0 ] ); } responseContainer = overwritten = undefined; }); // Delegate to script return "script"; } });
在讲ajaxConverter之前我们首先必须熟悉ajaxHandleResponse。为什么这么说呢,因为如果我们传入的dataType只有一个,如"json",那么你可能认为我们最后的s.dataType只有["json"],其实不是这样的,最后的格式其实是["text","json"]而这个text的加入就来自于ajaxHandleResponse函数。
(1)只要你传入的dataType是单个类型,都会经过他的处理,如"json","jsonp","html","script"最后都会变成["text","XX"]这种类型。那么他是怎么做到的呢?那么你必须了解通用ajaxTransport,他最后得到服务器的返回的数据类型是{"text":xhr.responseText}类型,所以在ajaxHandleResponse中得到的finalDataType就是text,而dataType[0]就是你传入的数据类型。上面我没有说用户传入*的情况,这种情况jQuery根据contentType来判断,在最后的dataTypers中会把*移除掉,转化成为服务器端返回的数据类型,如服务器返回json,那么最后的结果就是["text","json"]但是还是要弄懂通用ajaxTransport返回的数据是{"text":xhr.responseText}类型!
(2)ajaxHandleResponse的第二个作用就是把服务器的数据原封不动的传递给ajaxConverter,同时传入要转换的数据类型,如["text","json"]左后就会转化为json!
(3)题外话,post类型的ajax请求不会缓存,因为通过firebug将始终看到200,不会看到304状态码!
function ajaxHandleResponses( s, jqXHR, responses ) { var firstDataType, ct, finalDataType, type, contents = s.contents, dataTypes = s.dataTypes; // Remove auto dataType and get content-type in the process while ( dataTypes[ 0 ] === "*" ) {//如果用户传入的dataType是*就会经过这里的逻辑,最后通过mimeType或者返回的Content-Type来决定! dataTypes.shift();//如果服务器端返回的是json,那么ct就是application/json;charset=utf-8!shift就是把*移除!</span> if ( ct === undefined ) { ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); } } // Check if we're dealing with a known content-type if ( ct ) {//这时候根据content-type来处理,如application/json;charset=utf-8</span> for ( type in contents ) {//我们的contents中有"xml,html,json",content[type]是正则表达式,如果服务器返回json,那么满足正则! if ( contents[ type ] && contents[ type ].test( ct ) ) { dataTypes.unshift( type );//最后添加的dataTypes就是["json"],因为*已经被移除! break; } } } // Check to see if we have a response for the expected dataType //我们知道通用的ajaxTransport都是通过responseText方式返回的 //所以他的格式是responses={"text":xhr.responseText} //所以这里的,所以这里的if不会执行! if ( dataTypes[ 0 ] in responses ) { finalDataType = dataTypes[ 0 ]; } else { // Try convertible dataTypes //因为这里的type是"text",同时dataTypes[0]是json,s.converters["text json"] //所以finalDataType就是text! for ( type in responses ) { if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { finalDataType = type;//如果传入的是*,这里的finalDataType也是"text",所以最后的dataTypes就是["text json"] break; } if ( !firstDataType ) { firstDataType = type; } } // Or just use first one finalDataType = finalDataType || firstDataType; } // If we found a dataType // We add the dataType to the list if needed // and return the corresponding response if ( finalDataType ) { //finalDataType是text,dataTypes[0]是json //所以这里的dataTypes变成了["text json"] //unshift表示在最前面添加! if ( finalDataType !== dataTypes[ 0 ] ) { dataTypes.unshift( finalDataType );//这里才真正把dataTypes变成["text","XXX"]类型! } return responses[ finalDataType ]; } }
下面给出ajaxConverter的源码(通过上面的分析你应该知道了,dataTypes[1]是始终存在的!ajaxConverter就根据上面ajaxHandleReponse提供的dataTypes对数据进行转换):
function ajaxConvert( s, response, jqXHR, isSuccess ) { var conv2, current, conv, tmp, prev, converters = {}, // Work with a copy of dataTypes in case we need to modify it for conversion dataTypes = s.dataTypes.slice(); // Create converters map with lowercased keys if ( dataTypes[ 1 ] ) { for ( conv in s.converters ) { //converters["* text"]=window.String //converters["text html"]=true //converters["text json"]=jQuery.parseJSON //converters["text xml"]=jQuery.parseXML converters[ conv.toLowerCase() ] = s.converters[ conv ]; } } //拿出第一项,这里为text current = dataTypes.shift(); // Convert to each sequential dataType //拿出text第一项 while ( current ) { //第一次打印"html", //alert(s.responseFields[ current ] ); if ( s.responseFields[ current ] ) { //把jqXHR["responseHTML"]={} //jqXHR["responseText"]={} jqXHR[ s.responseFields[ current ] ] = response; } // Apply the dataFilter if provided if ( !prev && isSuccess && s.dataFilter ) { response = s.dataFilter( response, s.dataType ); } prev = current; //然后继续获取第一项,这时候prev是text,current是json current = dataTypes.shift(); if ( current ) { //current这时候是json //alert(current),如果第二项是*那么操作的还是第一项! // There's only work to do if current dataType is non-auto if ( current === "*" ) { current = prev; // Convert response if prev dataType is non-auto and differs from current } else if ( prev !== "*" && prev !== current ) { // Seek a direct converter conv = converters[ prev + " " + current ] || converters[ "* " + current ]; //这时候prev + " " + current="text json"所以conv=jQuery.parseJSON,所以下面不会走if语句! // If none found, seek a pair if ( !conv ) { //converters["* text"]=window.String //converters["text html"]=true //converters["text json"]=jQuery.parseJSON //converters["text xml"]=jQuery.parseXML for ( conv2 in converters ) { // If conv2 outputs current //把converters的键名全部按照空格切开! tmp = conv2.split( " " ); //如果切开过后的第二项和current相等,所以在传入的参数converters中,键值还是第二项最重要,因为第一项都是text没有实际的意义! //因为这里的current是json,所以一直会切换到第三个,也就是converters["text json"]才会满足if! //也就是说如果在converters里面第二项和我在dataTypes里面的第二项相同那么就满足if语句了! if ( tmp[ 1 ] === current ) { //加入上面的current是json,也就是dataType是"text json"那么这里切割的就是converters["text json"]=jQuery.parseJSON //temp[1]="json",temp[0]="text" //conv=converters["text text"]||converters["* text"] // If prev can be converted to accepted input conv = converters[ prev + " " + tmp[ 0 ] ] || converters[ "* " + tmp[ 0 ] ]; //conv存在 if ( conv ) { // Condense equivalence converters //表示dataType是"text html",那么这里就是converters[""] if ( conv === true ) { conv = converters[ conv2 ]; // Otherwise, insert the intermediate dataType } else if ( converters[ conv2 ] !== true ) { //current设置为"" current = tmp[ 0 ]; dataTypes.unshift( tmp[ 1 ] ); } break; } } }//End of for loop } //因为上面的conv不是true,conv=jQuery.parseJSON // Apply converter (if not an equivalence) if ( conv !== true ) { // Unless errors are allowed to bubble, catch and return them if ( conv && s[ "throws" ] ) { response = conv( response ); } else { try { //用找到的处理函数对结果进行处理 response = conv( response ); //因为我传入的dataType为"text json"所以直接用parseJSON进行处理得到了Object对象 //这也是为什么直接在$.ajax方法里面传入的"text json"那么返回的结果就是JSON的原因! //alert(response); } catch ( e ) { return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; } } } }//End of outer elseIf }//End of if }//End of while //(1)如果传入的是jsonp,那么不会进行处理,直接返回这个对象,这个对象的第一个键名是state键值是success //第二个键名是data键值是response!也就是对jsonp等数据不进行特殊的处理! //(2)如果传入的xml或者text,json等单个字符串,那么也不会把数据放入converters里面,因为dataType[1]为空!但是这时候为jqXHR["responseXML"]=response //jqXHR["responseText"]=response,jqXHR["responseJSON"]=response,其中response就是返回的数据!但是最后返回的数据还是和第一种情况是一样的! //(3)如果传入的是"text json",那么converters["* text"]=window.String,converters["text html"]=true,converters["text json"]=jQuery.parseJSON //converters["text xml"]=jQuery.parseXML,这时候current就是json,prev就是text,这时候conv=converters["text json"]=jQuery.parseJSON //然后把response数据传入conv中进行处理,然后返回数据形式{state:"success",data:"response"}不过这个response是已经解析为JSON的数据了! return { state: "success", data: response }; }总结:
(1)ajaxSetup如果没有第二个参数那么就是参数全部封装到jQuery.ajaxSettings上面。如果传入了第二个参数,那么首先把jQuery.ajaxSettings封装到第一个参数上,最后把第二个参数封装到上一步返回的对象上面,结果就是第一个参数具有了ajaxSetting和第二个参数的所有的方法和属性!
(2)对于"text script"类型jQuery自动添加了一个converter,这个converter是按照jQuery.globalEval方式来运行结果的!这就处理了dataType是script类型,但是服务器返回的是string类型,把string类型转换为js运行的逻辑!
(3)对于json,jsonp这种dataType来说,我们在内部直接将dataType[0]设置为"json",那么我们把服务器返回的数据按照parseJSON的方式进行处理!同时从上面的源码中也可以看到如果dataTypes为"script json"那么这种处理的函数处理逻辑其实也很简单。如果返回的有数据那么直接返回数据,如果没有数据,那么我们直接报错!
(4)我们要知道通过ajaxSetup或者ajaxPrefilter,ajaxTransport这种方式都会改变jQuery.ajaxSetting内置的converters数组!
(5)dataFilterFunction类型
指定处理响应的原始数据的回调函数。该函数还有两个参数:其一表示响应的原始数据的字符串,其二是dataType
属性字符串。这个函数就是在ajaxConverter里面被调用的,因为dataFilter方法生来就是为处理响应数据服务的,不过他只是对响应数据的预处理,真正的处理逻辑还要通过ajaxConverter函数内部的逻辑来完成,如数据类型的转换!从string类性转换为json等类型!
(6)该方法中首先判断是否含有dataType[1],目地在于判断如果是存在,那么就可以通过内置的converters来完成,因为内置的converters都是双项的!于是形成converters{"text html",true,"text xml":parseXML}这种对象可以直接处理双项的数据的转换!
"* text": String, // Text to html (true = no transformation) "text html": true, // Evaluate text as a json expression "text json": jQuery.parseJSON, // Parse text as xml "text xml": jQuery.parseXMLnote:如果上面的双项的数据类型不能处理,那么我们就进行数据类型切开,通过判断"text xml"中的xml是否和传入的dataTypes[1]相等,如果相等,那么构建新的转换器conv = converters[ prev + " " + tmp[ 0 ] ] ||converters[ "* " + tmp[ 0 ] ];来处理,所以他的逻辑还是很简单的!不过一般我们传入的还是"text xml"等固定的形式!这里只是为了让jQuery更加灵活一点!该方法的返回数据格式为: { state: "success", data: response }这种类型!state表示成功与否,data就是经过转换的数据!