jQuery源码分析之ajaxTransport

来源:互联网 发布:淘宝等级图 编辑:程序博客网 时间:2024/05/22 05:13

其实际上和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 dataTypeExpressionwhile ( (dataType = dataTypes[i++]) ) {// Prepend if requestedif ( 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]();//打印f1prefilters["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 requestsif ( 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 browsersscript.onload = script.onreadystatechange = function( _, isAbort ) {if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {// Handle memory leak in IEscript.onload = script.onreadystatechange = null;// Remove the scriptif ( script.parentNode ) {script.parentNode.removeChild( script );}// Dereference the scriptscript = null;// Callback if not abortif ( !isAbort ) {callback( 200, "success" );}}};// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending// Use native DOM manipulation to avoid our domManip AJAX trickeryhead.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//如果没有执行跨域,但是支持corsif ( !options.crossDomain || support.cors ) {var callback;return {//send方法send: function( headers, complete ) {var i,//获取xhr对象xhr = options.xhr(),//保存xhr的数量id = ++xhrId;                                             //打开socket!// Open the socketxhr.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 neededif ( 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 headersfor ( 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 );// Listenercallback = function( _, isAbort ) {var status, statusText, responses;                                      //如果callbacks存在,同时isAbort为false表示没有放弃执行// Was never called and is aborted or completeif ( callback && ( isAbort || xhr.readyState === 4 ) ) {// Clean up//清除回调!同时从xhrCallbacks中清除!delete xhrCallbacks[ id ];callback = undefined;//将事件变化函数绑定为空函数xhr.onreadystatechange = jQuery.noop;                                          //如果要移除,而且readyState不是4,那么手动调用abort方法// Abort manually if neededif ( 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 requeststry {statusText = xhr.statusText;} catch( e ) {// We normalize with Webkit giving an empty statusTextstatusText = "";}// 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 neededif ( responses ) {complete( status, statusText, responses, xhr.getAllResponseHeaders() );}};                  //如果没有指定async那么直接调用callback方法!if ( !options.async ) {// if we're in sync mode we fire the callbackcallback();} 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 callbacksetTimeout( callback );} else {// Add to the list of active xhr callbacks//var xhrId = 0,//xhrCallbacks = {}//xhrCallbacks[i]=callback说明这个对象是为了保存回调函数的!xhr.onreadystatechange = xhrCallbacks[ id ] = callback;}},                 //如果调用abort方法,那么回调的时候第一个参数是undefined,第二个参数是trueabort: 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方法里面发送出去!

0 0
原创粉丝点击