prefilters源码分析(主要作用在于调用send之前对HTTP头等进行修改和进一步处理):
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),可以看到内部直接调用了addToPrefiltersOrTransports方法
var rnotwhite = (/\S+/g); function addToPrefiltersOrTransports( structure ) { //该函数可以传递两个参数,第一个是dataType组成的字符串,用空格隔开 //第二个参数是回调函数! // dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { //如果传入的dataTypeExpression不是string,记住dataTypeExpression默认为"*" //那么表示只是传入了一个参数,该参数就是回调函数! if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, //把dataTypeExpression修改为一个数组! dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || []; //如果传入的第二个参数是函数 if ( jQuery.isFunction( func ) ) { // For each dataType in the dataTypeExpression while ( (dataType = dataTypes[i++]) ) { // Prepend if requested //如果dataType第一个字符是+,那么获取+以后的部分! if ( dataType.charAt( 0 ) === "+" ) { dataType = dataType.slice( 1 ) || "*"; //shift用于移除数组中第一项并且返回该项,unshift用于在数组前面添加任意项并且返回数组长度! //如果要过滤"html jsonp"那么一开始传入structure是空对象,那么structure["html"]=[],然后把这个回调函数 //插入到数组的最前面,记住这里传入的structure是一个引用,所以当你这里修改为structure["html"]=[func]时候那么 //原来的prefilters对象也已经修改了!记住,如果是+调用的是unshift那么是放在数组最前面的! (structure[ dataType ] = structure[ dataType ] || []).unshift( func ); //如果没有+那么是放在回调数组的后面的! // Otherwise append } else { (structure[ dataType ] = structure[ dataType ] || []).push( func ); } } } }; }测试代码1:
var func=function(){alert(1);} var prefilters={}; //返回匿名函数 var resultFunc=addToPrefiltersOrTransports(prefilters); //这时候prefilters里面已经被添加了func函数了 //所以prefilters["jsonp"]=[func],prefilters["html"]=[func] resultFunc("jsonp html",func); //里面存储的是函数,可以直接调用! prefilters["jsonp"][0]();
测试代码2:(jQuery对scripts类型的预先处理)
var prefilters={}; var options={}; jQuery.ajaxPrefilter1=addToPrefiltersOrTransports(prefilters); jQuery.ajaxPrefilter1( "script", function( s ) { if ( s.cache === undefined ) { s.cache = false; } if ( s.crossDomain ) {//记住,如果是跨域请求那么不会相应全局ajax事件! s.type = "GET"; s.global = false; } }); //这是prefilters["scripts"]是一个数组,获取第0项就是我上面添加的函数,然后执行! alert(prefilters["script"][0](options));
默认值:jQuery智能猜测,猜测范围(xml、 json、 script或html)
指定返回的数据类型。该属性值可以为:
cache
参数。注意:在远程请求时(不在同一个域下),所有POST请求都将转为GET请求。(因为将使用DOM的script标签来加载)null
或{}
。设置该请求加载的脚本文件的字符集。只有当请求时dataType为"jsonp"或"script",并且type是"GET"才会用于强制修改charset(其实必须是跨域请求才会修改scriptCharset)。这相当于设置<script>标签的charset属性。通常只在当前页面和远程数据的内容编码不同时使用。ajaxPrefilters在处理"jsonp,json"的时候返回了一个"script",最终导致在insprctPrefiltersOrTransport中也会对"script"进行处理,也就是调用ajaxPrefilters中关于"script"的拦截器,执行"script"中的处理函数集合!进而也达到:如果没有明确指定cache,那么jsonp不会缓存;如果跨域,那么type就是get,同时ajax全局事件失效;但是这仅仅是ajaxPrefilters中的对HTTP头的处理,最终在transport中还是用了通用的ajaxTransport,因为没有对于"jsonp"的特殊的ajxTransport,所以最后得到的还是一个字符串,然后通过ajaxConverter对这个字符串进行了修改,执行得到的jsonp请求结果!
// Detect, normalize options and install callbacks for jsonp requests jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { var callbackName, overwritten, responseContainer, jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? url" ://指定了是jsonp请求,同时url也符合jsonp规范,那么jsonProp就是字符串"url"。 //否则,只有data是string,contentType是x-www-form-urlencoded,同时s.data符合特定的规范时候才会是data字符串 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"; // Install callback overwritten = window[ callbackName ];//把自己设置的或者jQuery默认的回调函数保存下来,进而得到数据时候回调! window[ callbackName ] = function() {//之所以要重写一个函数,是为了回调时候获取到服务器数据,而且是类数组的数据格式! responseContainer = arguments; }; // Clean-up function (fires after converters) jqXHR.always(function() {//这个方法加入到jqXHR的always中,表示不管对服务器的请求是失败还是成功都应该回调! // Restore preexisting value window[ callbackName ] = overwritten;//重新载入回调函数,也就是我们设置的或者jQuery默认的回调函数! // 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 );//这句if,使得我们成功或者失败时候重新保证我们没有对options修改掉! } // Call if it was a function and we have a response if ( responseContainer && jQuery.isFunction( overwritten ) ) { overwritten( responseContainer[ 0 ] );//真正回调,因为服务器返回的参数是字符串,该字符串已经在arguments中! }//这个回调结束了,那么jsonp请求才算真正完成了!只要resolve或者reject调用时候就会触发! responseContainer = overwritten = undefined; }); // Delegate to script return "script"; } });
var rjsonp = /(=)\?(?=&|$)|\?\?/; var s={ url:"http://localhost:8080/qinl/a.action?name=?",/*相当于指定了jsonp函数,但是jsonCallback函数必须自己手动添加上去*/ jsonpCallback:"qinliang" }; console.log( rjsonp.test( s.url ));/*打印true,表示符合jsonp的URL格式,只是jsonp函数已经在URL中指定了!*/ /*对URL重新设置,添加jsonCallback函数*/ var result=s["url"].replace(/(=)\?(?=&|$)|\?\?/,"$1" + s.jsonpCallback ); /*打印http://localhost:8080/qinl/a.action?name=qinliang*/ console.log(result);测试代码2:
var rquery = (/\?/); var rjsonp = /(=)\?(?=&|$)|\?\?/; var s={ jsonp:"onjsonload", url:"a.action?name=?", callbackNames:"invoke" } //打印a.action?name=?&onjsonload=invoke var result=s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + s["callbackNames"]; alert(result);
note:在这个函数内部我们是对jsonp的请求的url进行了处理,得到正确的URL进行访问。解决的特殊情况:如果用户URL后有问号,那么添加&符号就行!
ajaxPrefilters中函数的执行:
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { var inspected = {}, seekingTransport = ( structure === transports ); function inspect( dataType ) { var selected; inspected[ dataType ] = true; jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { options.dataTypes.unshift( dataTypeOrTransport ); inspect( dataTypeOrTransport ); return false; } else if ( seekingTransport ) { return !( selected = dataTypeOrTransport ); } }); return selected; } //(1)text类型没有prefilters,接下来处理*类型的回调函数!所以我们还是直接指定"json"等单个字符串,而不要是"text json"这种类型,否则只会执行text的prefilters, //而text的prefiltes是不存在的,更加糟糕的是prefiltes也没有是*的这种类型,所以prefilters会形同虚设! //(2)ajaxTransport类型同样的道理也不要设置为"text json"而要设置为"json"这种格式。如果没有json这种ajaxTransport那么就会处理*这种类型,但是json这种存在! //ajaxPrefiltes处理script和json/jsonp格式;ajaxTransport处理script和*这种类型! return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); }note: 我们指定数据类型的时候还是应该指定"json"而不要指定"text json"否则所有的关于json的prefilters形同虚设!ajaxPrefilters处理script/json/jsonp类型,而ajaxTransport处理了script/*这种类型!
(1)这里调用addToPrefiltersOrTransports方法时候是返回一个匿名函数,传入的参数会被匿名函数保存起来,这就是闭包!同时,在函数里面修改了这个prefilters对象那么外层的函数是可以检测到的,否则ajax方法里面就无法调用这些预处理函数了!
(2)如果在给prefilters方法,也就是匿名函数传入的第一个字符串参数前面有加号,那么内部会调用unshift把这个函数放在数组的最前面,如果没有加号那么就只是在数组里面添加
(3)(structure[ dataType ] = structure[ dataType ] || [])代码告诉我们这个每一个类型如html类型的预处理函数是一个数组,这个数组里面会存放很多的预处理函数!
(4)
用于预处理参数选项的回调函数。它有以下3个参数:
options
:(Object对象)当前AJAX请求的所有参数选项。originalOptions
:(Object对象)传递给$.ajax()方法的未经修改的参数选项。jqXHR
:当前请求的jqXHR对象(经过jQuery封装的XMLHttpRequest对象)。