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

jQuery源码分析之ajaxConverter与ajaxHandleResponse函数

2015-11-02 14:32 661 查看
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"那么结果就是直接执行!

如果传入的dataType是"jsonp"那么会怎么处理呢,看下面的源码:

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.parseXML
note:如果上面的双项的数据类型不能处理,那么我们就进行数据类型切开,通过判断"text xml"中的xml是否和传入的dataTypes[1]相等,如果相等,那么构建新的转换器conv = converters[ prev + " " + tmp[ 0 ] ] ||converters[ "* " + tmp[ 0 ] ];来处理,所以他的逻辑还是很简单的!不过一般我们传入的还是"text xml"等固定的形式!这里只是为了让jQuery更加灵活一点!该方法的返回数据格式为:{
state: "success", data: response }这种类型!state表示成功与否,data就是经过转换的数据!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: