jquery中的$.ajax()的源码分析

针对获取到location.href的兼容代码:

  1. try {
  2. ajaxLocation = location.href;
  3. } catch( e ) {
  4. // Use the href attribute of an A element
  5. // since IE will modify it given document.location
  6. ajaxLocation = document.createElement( "a" );
  7. ajaxLocation.href = "";
  8. ajaxLocation = ajaxLocation.href;
  9. }

note:如果通过location.href获取地址出错,那么我们就通过创建A标签,然后获取该标签的href!在IE中可以打印主机名,如"http://locahost:8080/"
关于去除URL中的hash值,同时兼容IE7,如果没有协议字段我们要手动添加:

  1. var ajaxLocation="http://localhost:8080/qinl/a.action?#23"
  2. var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/;
  3. var rhash = /#.*$/;
  4. //匹配开头的"//"字段
  5. var rprotocol = /^\/\//;
  6. //获取前面的协议字段,如"http:","file:"等
  7. var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
  8. //第一个replace去掉用hash值,第二个replace表示如果去掉hash值以后开头已经是//那么也要进行相应的处理
  9. var result=ajaxLocation.replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );

匹配是否跨域请求的部分:

  1. //测试代码1:
  2. var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/; //打印[http://localhost:8080,http:,localhost,8080]
  3. alert(rurl.exec("http://localhost:8080/qinl/xx.action"));
  4. //测试代码2:
  5. //http://www.365mini.com/diy.php?f=jquery_ajax-demo
  6. var ajaxLocation=location.href;
  7. var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
  8. //打印[http://www.365mini.com,http:,www.365mini.com,]
  9. alert(ajaxLocParts);

首先来一段精简版的$.ajax方法:也就是其中的原理

  1. var completeDeferred = jQuery.Callbacks("once memory");
  2. var dfd=new $.Deferred();
  3. var jqXHR={
  4. }
  5. //jqXHR有了promise所有方法,但是修改状态还是要靠Deferred对象!
  6. //同时为这个jqXHR封装一个Callbacks对象,调用complete就想相当于向其中添加
  7. //回调函数,然后要触发他就直接调用fireWith才行!
  8. dfd.promise(jqXHR).complete=completeDeferred.add;
  9. var f1=function()
  10. {
  11. alert("f1");
  12. }
  13. //为done和complete添加回调函数
  14. jqXHR.done(f1).complete(f1);
  15. //调用fire触发所有的complete添加的回调
  16. completeDeferred.fire();
  17. //触发Deferred对象的所有的done集合中函数,记住这里
  18. //不能是dfd调用,因为他没有resolve方法不能改变状态!
  19. dfd.resolve();

ajax源码分析:

  1. ajax: function( url, options ) {
  2. //ajax方法参数调整,如果url是object,这是我们一般的调用方式
  3. // If url is an object, simulate pre-1.5 signature
  4. if ( typeof url === "object" ) {
  5. options = url;
  6. url = undefined;
  7. }
  8. //option是一个对象
  9. // Force options to be an object
  10. options = options || {};
  11. var // Cross-domain detection vars
  12. parts,
  13. // Loop variable
  14. i,
  15. // URL without anti-cache param
  16. cacheURL,
  17. // Response headers as string
  18. responseHeadersString,
  19. // timeout handle
  20. timeoutTimer,
  21. // To know if global events are to be dispatched
  22. fireGlobals,
  23. transport,
  24. // Response headers
  25. responseHeaders,
  26. // Create the final options object
  27. s = jQuery.ajaxSetup( {}, options ),
  28. // Callbacks context
  29. //设置context,如果没有context那么就是返回的最终的options=ajaxSettings+options(用户调用ajax方法时候传送的option)
  30. callbackContext = s.context || s,
  31. //如果传入的对象有context,同时context是DOM对象或者是jQuery对象,那么把该DOM对象封装为jQuery对象
  32. //如果不满足也就是没有context或者context不是DOM对象和jQuery对象,那么globalEventContext就是jQuery.event对象!
  33. // Context for global events is callbackContext if it is a DOM node or jQuery collection
  34. globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
  35. jQuery( callbackContext ) :
  36. jQuery.event,
  37. //创建Deferred对象
  38. // Deferreds
  39. deferred = jQuery.Deferred(),
  40. //创建Callbacks对象
  41. completeDeferred = jQuery.Callbacks("once memory"),
  42. //获取最终options的statusCode参数,默认是空对象!
  43. // Status-dependent callbacks
  44. statusCode = s.statusCode || {},
  45. // Headers (they are sent all at once)
  46. requestHeaders = {},
  47. requestHeadersNames = {},
  48. // The jqXHR state
  49. state = 0,
  50. // Default abort message
  51. strAbort = "canceled",
  52. //创建一个伪的xhr对象,该对象有readyState,getResponseHeader,getAllResponseHeaders,setRequestHeader
  53. //overrideMimeType,statusCode,abort方法和属性!
  54. // Fake xhr
  55. jqXHR = {
  56. readyState: 0,
  57. // Builds headers hashtable if needed
  58. getResponseHeader: function( key ) {
  59. var match;
  60. //状态是2的时候才能获取数据
  61. if ( state === 2 ) {
  62. if ( !responseHeaders ) {
  63. responseHeaders = {};
  64. //rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
  65. //responseHeaders的键名就是第一个捕获组的数据,第二个键值就是第二个捕获组数据!
  66. while ( (match = rheaders.exec( responseHeadersString )) ) {
  67. responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
  68. }
  69. }
  70. //返回这个key对应的键值!
  71. match = responseHeaders[ key.toLowerCase() ];
  72. }
  73. return match == null ? null : match;
  74. },
  75. // Raw string
  76. //如果状态是2,那么就是responseHeadersString
  77. getAllResponseHeaders: function() {
  78. return state === 2 ? responseHeadersString : null;
  79. },
  80. // Caches the header
  81. //设置HTTP请求头的时候,头是小写的
  82. setRequestHeader: function( name, value ) {
  83. var lname = name.toLowerCase();
  84. //如果state为0那么就缓存头,把结果放入requestHeaders中!但是要提前查找requestHeadersNames
  85. if ( !state ) {
  86. name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
  87. requestHeaders[ name ] = value;
  88. }
  89. return this;
  90. },
  91. // Overrides response content-type header
  92. //如果state=0那么可以覆盖这个mimetype!
  93. overrideMimeType: function( type ) {
  94. if ( !state ) {
  95. s.mimeType = type;
  96. }
  97. return this;
  98. },
  99. // Status-dependent callbacks
  100. statusCode: function( map ) {
  101. var code;
  102. if ( map ) {
  103. if ( state < 2 ) {
  104. for ( code in map ) {
  105. // Lazy-add the new callback in a way that preserves old ones
  106. statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
  107. }
  108. } else {
  109. // Execute the appropriate callbacks
  110. jqXHR.always( map[ jqXHR.status ] );
  111. }
  112. }
  113. return this;
  114. },
  115. // Cancel the request
  116. //取消请求
  117. abort: function( statusText ) {
  118. var finalText = statusText || strAbort;
  119. if ( transport ) {
  120. transport.abort( finalText );
  121. }
  122. done( 0, finalText );
  123. return this;
  124. }
  125. };
  126. // Attach deferreds
  127. //让jqXHR具有promise的所有的属性和方法!不包括状态改变的方法,如resollveWith等
  128. //同时jqXHR的complete对应于completeDeferred的add方法,但是该jqXHR中也封装了三个Callbacks对象
  129. //但是这里没有用内部的Callbacks对象,而是采用一个新的Callbacks对象
  130. //completeDeferred = jQuery.Callbacks("once memory"),
  131. deferred.promise( jqXHR ).complete = completeDeferred.add;
  132. //success调用的promise对象内置的done方法对应于的Callbacks对象
  133. jqXHR.success = jqXHR.done;
  134. //error方法调用的promise对象内置的fail方法对应的Callbacks对象!
  135. //注意:这个内置的promise对象的progress方法对应的Callbacks对象没有用到!
  136. jqXHR.error = jqXHR.fail;
  137. // Remove hash character (#7531: and string promotion)
  138. // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
  139. // Handle falsy url in the settings object (#10093: consistency with old signature)
  140. // We also use the url parameter if available
  141. s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
  142. //type就是get或者post。这个方式可以通过用户传入的对象的method或者type或者最终的对象的method或者type获取!
  143. // Alias method option to type as per ticket #12004
  144. s.type = options.method || options.type || s.method || s.type;
  145. // Extract dataTypes list
  146. //取出dataType两边的空格,同时通过空格进行分组得到一个数组!dataType="html"
  147. s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
  148. //如果没有crossDomain对象
  149. // A cross-domain request is in order when we have a protocol:host:port mismatch
  150. if ( s.crossDomain == null ) {
  151. parts = rurl.exec( s.url.toLowerCase() );
  152. //如果在同一个域名里面那么这里的判断都是false,结果就是crossDomain为false
  153. //如果不再同一个域名里面,那么这里的判断都是true,结果就是crossDomain为true!
  154. s.crossDomain = !!( parts &&
  155. ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
  156. ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
  157. ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
  158. );
  159. }
  160. //如果存在data同时存在processData同时data不是string!
  161. //traditional是为了兼容jQuery<1.3.2行为的!
  162. // Convert data if not already a string
  163. if ( s.data && s.processData && typeof s.data !== "string" ) {
  164. s.data = jQuery.param( s.data, s.traditional );
  165. }
  166. // Apply prefilters
  167. inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
  168. //如果在预过滤器中已经终止了请求,那么直接返回jqXHR对象!
  169. // If request was aborted inside a prefilter, stop there
  170. if ( state === 2 ) {
  171. return jqXHR;
  172. }
  173. // We can fire global events as of now if asked to
  174. // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
  175. //如果是global参数,那么我们直接trigger事件ajaxStart!
  176. fireGlobals = jQuery.event && s.global;
  177. // Watch for a new set of requests
  178. if ( fireGlobals && jQuery.active++ === 0 ) {
  179. jQuery.event.trigger("ajaxStart");
  180. }
  181. // Uppercase the type
  182. //把type变成大写
  183. s.type = s.type.toUpperCase();
  184. // Determine if request has content
  185. //rnoContent = /^(?:GET|HEAD)$/
  186. //也就是如果没有指定type也就是请求方式!
  187. s.hasContent = !rnoContent.test( s.type );
  188. // Save the URL in case we're toying with the If-Modified-Since
  189. // and/or If-None-Match header later on
  190. //获取url参数!
  191. cacheURL = s.url;
  192. // More options handling for requests with no content
  193. //如果指定了请求方式,如get,post等!
  194. if ( !s.hasContent ) {
  195. //没有指定请求方式的时候有传递数据!
  196. // If data is available, append data to url
  197. if ( s.data ) {
  198. //var rquery = (/\?/);
  199. //如果url后面有问号,那么直接把参数绑定到问号后面就可以了!否则添加问号在绑定!
  200. //同时删除数据!
  201. cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
  202. // #9682: remove data so that it's not used in an eventual retry
  203. delete s.data;
  204. }
  205. // Add anti-cache in url if needed
  206. //如果指定了cache为false表示不能进行数据缓存,那么会在url后面添加一个当前时间!
  207. if ( s.cache === false ) {
  208. s.url = rts.test( cacheURL ) ?
  209. // If there is already a '_' parameter, set its value
  210. //var nonce = jQuery.now();
  211. cacheURL.replace( rts, "$1_=" + nonce++ ) :
  212. // Otherwise add one to the end
  213. cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
  214. }
  215. }
  216. // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  217. //如果添加了ifModified头
  218. //var lastModified={}
  219. if ( s.ifModified ) {
  220. //如果lastModified保存了这个cacheURL也就是这个URL有缓存了!那么直接添加头If-Modified-Since数据为
  221. //jQuery.lastModified[ cacheURL ]获取到的数据!
  222. if ( jQuery.lastModified[ cacheURL ] ) {
  223. jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
  224. }
  225. //如果在etag: {}中保存了这个URL
  226. //那么添加If-None-Match,因为Etag和if-None-Match是一对,Last-Modified和If-Modified-Since是一对!
  227. if ( jQuery.etag[ cacheURL ] ) {
  228. jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
  229. }
  230. }
  231. // Set the correct header, if data is being sent
  232. //如果有数据传送,同时也指定了get,post方法,同时contentType也指定!
  233. //那么添加一个头Content-Type!
  234. if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
  235. jqXHR.setRequestHeader( "Content-Type", s.contentType );
  236. }
  237. // Set the Accepts header for the server, depending on the dataType
  238. //同时添加请求头Accept
  239. //(1)如果指定了dataType,同时accepts中dataType存在,也就是必须是指定的data[type]
  240. jqXHR.setRequestHeader(
  241. "Accept",
  242. s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
  243. //  allTypes = "*/".concat("*");
  244. //如果支持的数据类型是内置的类型,那么获取内置的值,如text获取的就是"text/plain"
  245. //同时dataTypes[0]不是"*",那么我们加上一个逗号,同时加上后面剩余的部分!
  246. /*
  247. var allTypes = "*/".concat("*");
  248. //打印
  249. //alert(allTypes);
  250. //最后的格式就是:text/html,*/*;q=0.01
  251. //如果传入的dataType就是*,那么最后的结果就是"*/*"
  252. s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
  253. s.accepts[ "*" ]
  254. );
  255. // Check for headers option
  256. //如果还定义了headers选项,那么会被逐个发送到服务器端!
  257. for ( i in s.headers ) {
  258. jqXHR.setRequestHeader( i, s.headers[ i ] );
  259. }
  260. // Allow custom headers/mimetypes and early abort
  261. //如果指定了beforeSend,同时beforeSend的函数调用的结果是false或者state是2,那么取消这次请求
  262. //beforeSend中传入的参数为callbackContext = s.context || s也就是最终对象的context参数为上下文
  263. //第一个参数是XHR对象,第二个参数是最终的options对象!
  264. if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
  265. // Abort if not done already and return
  266. return jqXHR.abort();
  267. }
  268. // aborting is no longer a cancellation
  269. strAbort = "abort";
  270. // Install callbacks on deferreds
  271. //往jqXHR["success"]和jqXHR["error"],jqXHR["complete"]中添加回调函数
  272. //回调函数就是通过最终options对象获取到的success,error,complete函数!
  273. for ( i in { success: 1, error: 1, complete: 1 } ) {
  274. jqXHR[ i ]( s[ i ] );
  275. }
  276. // Get transport
  277. //传入的参数是transports对象!这个函数里面会判断是否传入了transports
  278. transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
  279. // If no transport, we auto-abort
  280. if ( !transport ) {
  281. done( -1, "No Transport" );
  282. } else {
  283. //如果有transport那么readyState就是1,表示 (载入)已调用send()方法,正在发送请求,也就是请求的发送是在
  284. //inspectPrefiltersOrTransports里面完成的!
  285. jqXHR.readyState = 1;
  286. // Send global event
  287. //指示是否触发全局Ajax事件。将该值设为false将阻止全局事件处理函数被触发
  288. //fireGlobals = jQuery.event && s.global;
  289. //如果是表示全局ajax事件,那么我们要调用ajaxSend方法!同时为这个方法传入参数jqXHR和最终option!
  290. if ( fireGlobals ) {
  291. globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
  292. }
  293. // Timeout
  294. //如果指定了async同时timeout>0表示指定了隔多少秒就放弃
  295. //一个超时调用,超时直接调用abort方法!
  296. if ( s.async && s.timeout > 0 ) {
  297. timeoutTimer = setTimeout(function() {
  298. jqXHR.abort("timeout");
  299. }, s.timeout );
  300. }
  301. //如果有transport,那么调用send方法!
  302. try {
  303. state = 1;
  304. transport.send( requestHeaders, done );
  305. } catch ( e ) {
  306. // Propagate exception as error if not done
  307. if ( state < 2 ) {
  308. done( -1, e );
  309. // Simply rethrow otherwise
  310. } else {
  311. throw e;
  312. }
  313. }
  314. }

总结:

(1)调用 jQuery.ajaxSetup( {}, options )让最终options具有jQuery内置的所有的属性,同时也包括用户调用ajax方法的时候传入的所有的属性和方法!

(2)创建jqXHR对象,让该对象具有Deferred的所有属性和方法,该Deferred对象可以绑定用户的success和error方法。但是用户传入的compelte方法表示任何情况下都会调用,我们就引入了一个Callbacks对象,把complete回调函数存入该Callback中(用fireWith调用)

(3)对URL处理,取出hash加上协议名称,为type赋值,也就是Get/Post方法(用户可以通过method或者type传入该方法);指定dataTypes说明用户需要要的数据类型(用户通过dataType传入);如果用户没有明确指定crossDomain,那么自己判断,如果用户传入的URL也就是访问的URL和当前的location.href不相同(包括协议名称,主机名,端口号等),那么直接把crossDomain设置为true;如果传入了数据,也就是data那么通过 jQuery.param方法把这个数据序列化;

(4)上述步骤完成以后,我们就调用inspectPrefiltersOrTransports,这个方法传入了prefilters,表示对prefilters中所有的预处理函数进行检测,该方法可以修改前面所有的参数,当然也可以添加新的信息!(这里是prefilters)

(5)如果用户传入了global参数,那么我们在这个步骤执行"ajaxStart"事件

globalBoolean类型

默认值:true。指示是否触发全局Ajax事件。将该值设为false将阻止全局事件处理函数被触发,例如ajaxStart()和ajaxStop()。它可以用来控制各种Ajax事件。

(6)如果指定了get/head请求,那么如果有数据那么把数据绑定到URL后面(同时保存这个URL以便缓存URL)。同时如果是指定了get/head时候还明确指定了不能缓存数据,那么我们把缓存的URL后面添加一个随机数,随机数是当前时间!(一开始设定了缓存URL是用户传入的ur,get/head请求等都会对URL修改)

(7)如果用户指定了ifModified,表示只有数据改变时候才发送请求。如果这个URL已经访问过了,那么我们取出访问该URL时候服务器给的etag和if-none-match标签,并且把他们通过"If-None-Match和If-Modified-Since形式发送给服务器端,让服务器端去判断数据有没有改变。这两个头是在done方法中,也就是成功回调时候设置的!

ifModifiedBoolean类型

默认值:false。允许当前请求仅在服务器数据改变时获取新数据(如未更改,浏览器从缓存中获取数据)。它使用HTTP头信息Last-Modified来判断。从jQuery 1.4开始,他也会检查服务器指定的'etag'来确定数据是否已被修改。

(8)设置数据类型content-type,把content-type的头添加到jqXHR对象上

contentTypeString类型

默认值:'application/x-www-form-urlencoded; charset=UTF-8'。使用指定的内容编码类型将数据发送给服务器。W3C的XMLHttpRequest规范规定charset始终是UTF-8,你如果将其改为其他字符集,也无法强制浏览器改字符编码。

(9)设置accept头,告诉服务器浏览器能够接受的数据类型

acceptsObject类型

默认值:取决于dataType属性。发送的内容类型请求头,用于告诉服务器——浏览器可以接收服务器返回何种类型的响应。如果传入的是"*"结果就是"*/*",否则就是如格式"text/html,*/*;q=0.01"

(10)设置用户通过headers传入的HTTP头

headersObject类型1.5 新增

默认值:{}。以对象形式指定附加的请求头信息。请求头X-Requested-With: XMLHttpRequest将始终被添加,当然你也可以在此处修改默认的XMLHttpRequest值。headers中的值可以覆盖beforeSend回调函数中设置的请求头(意即beforeSend先被调用)。

(11)调用beforeSend

beforeSendFunction类型

指定在请求发送前需要执行的回调函数。该函数还有两个参数:其一是jqXHR对象,其二是当前settings对象。这是一个Ajax事件,如果该函数返回false,将取消本次ajax请求。

(12)这一步才把我们传入的success,error,compelete放入相应的Deferred对象和Callback对象里面,以备回调!

(13)这一步是重点:调用transport里面所有的函数集合。函数调用的返回结果是一个对象,该对象有send和abort方法。调用send方法就是真正的向服务器发送数据,如果没有得到transport对象那么表示请求失败。如果得到了这个对象,那么我们把readyState设置为1,然后调用send方法,但是调用send方法之前我们要调用ajaxSend方法!在send方法调用时候transport.send( requestHeaders, done );我们传入了回调函数done方法,该方法处理了回调的逻辑!

我们看看下面的done方法的处理逻辑:

  1. function done( status, nativeStatusText, responses, headers ) {
  2. var isSuccess, success, error, response, modified,
  3. statusText = nativeStatusText;
  4. // Called once
  5. //如果state是2,那么直接返回!
  6. if ( state === 2 ) {
  7. return;
  8. }
  9. // State is "done" now
  10. //state设置为2表示不会再次执行了!
  11. state = 2;
  12. // Clear timeout if it exists
  13. //如果timeoutTimer存在,那么直接清除!
  14. if ( timeoutTimer ) {
  15. clearTimeout( timeoutTimer );
  16. }
  17. // Dereference transport for early garbage collection
  18. // (no matter how long the jqXHR object will be used)
  19. transport = undefined;
  20. // Cache response headers
  21. //获取response的头部信息,默认是空!
  22. responseHeadersString = headers || "";
  23. // Set readyState
  24. //如果status>0那么把readyState设置为4!
  25. jqXHR.readyState = status > 0 ? 4 : 0;
  26. // Determine if successful
  27. //如果status在指定的区间内那么表示成功!
  28. isSuccess = status >= 200 && status < 300 || status === 304;
  29. // Get response data
  30. //如果done方法有responses那么对他进行处理!
  31. if ( responses ) {
  32. response = ajaxHandleResponses( s, jqXHR, responses );
  33. }
  34. // Convert no matter what (that way responseXXX fields are always set)
  35. response = ajaxConvert( s, response, jqXHR, isSuccess );
  36. // If successful, handle type chaining
  37. if ( isSuccess ) {
  38. // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  39. //如果ifModified存在,那么就要设置If-Modified-Since和If-None-Match头!
  40. if ( s.ifModified ) {
  41. modified = jqXHR.getResponseHeader("Last-Modified");
  42. if ( modified ) {
  43. jQuery.lastModified[ cacheURL ] = modified;
  44. }
  45. modified = jqXHR.getResponseHeader("etag");
  46. if ( modified ) {
  47. jQuery.etag[ cacheURL ] = modified;
  48. }
  49. }
  50. // if no content
  51. //204表示没有数据,这时候页面就不需要跳转!还是停留在当前页面!
  52. if ( status === 204 || s.type === "HEAD" ) {
  53. statusText = "nocontent";
  54. //如果是304那么表示没有修改内容!
  55. // if not modified
  56. } else if ( status === 304 ) {
  57. statusText = "notmodified";
  58. //如果有数据,那么我们获取到数据!
  59. // If we have data, let's convert it
  60. } else {
  61. statusText = response.state;
  62. success = response.data;
  63. error = response.error;
  64. isSuccess = !error;
  65. }
  66. } else {
  67. //这里的else表示请求失败!我们从statusText获取到错误的信息,然后对statusText进行处理!
  68. // We extract error from statusText
  69. // then normalize statusText and status for non-aborts
  70. error = statusText;
  71. if ( status || !statusText ) {
  72. statusText = "error";
  73. if ( status < 0 ) {
  74. status = 0;
  75. }
  76. }
  77. }
  78. //为jqXHR设置数据
  79. // Set data for the fake xhr object
  80. jqXHR.status = status;
  81. jqXHR.statusText = ( nativeStatusText || statusText ) + "";
  82. // Success/Error
  83. if ( isSuccess ) {
  84. //如果成功了请求,那么我们传入的Context是callbackContext,传入的数据是response.data
  85. //response.status和jqXHR对象
  86. deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
  87. } else {
  88. deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
  89. }
  90. // Status-dependent callbacks
  91. jqXHR.statusCode( statusCode );
  92. statusCode = undefined;
  93. //如果是全局执行
  94. if ( fireGlobals ) {
  95. globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
  96. [ jqXHR, s, isSuccess ? success : error ] );
  97. }
  98. // Complete
  99. //这个对象添加的所有函数执行,表示完成,不是成功,失败,而是complelte表示不管成功与否都是会执行的!
  100. //而且只会执行一次!
  101. completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
  102. if ( fireGlobals ) {
  103. //globalEventContext也就是最终options的事件,触发事件ajaxComplete!
  104. globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
  105. // Handle the global AJAX counter
  106. //如果全局的ajax计数器已经是0了,那么就会触发ajaxStrop事件!
  107. if ( !( --jQuery.active ) ) {
  108. jQuery.event.trigger("ajaxStop");
  109. }
  110. }
  111. }
  112. return jqXHR;
  113. }
  114. );

note:

(1)state在send调用之前为1,在done方法调用的时候设置为2,默认为0.所以2表示已经回调成功了,1表示send方法已经调用但是还没有回调。

(2)调用顺序是ajaxStart,ajaxSend,ajaxSuccess/ajaxError,ajaxComplete,ajaxStop这就是全局事件的调用顺序!
(3)在done方法中通过resolveWith,rejectWith来触发success,error事件,通过fireWith来触发compelete事件

(4)返回真实的服务器端数据,如responseText服务器端的数据!ajaxHandleResponses作用:把服务器端返回的数据封装到jqXHR对象上面,形成jqXHR["responseText"]=xhr.responseText这种类型!同时把responses中的相应的数据取出来。因为responses={"text":xhr.responseText}是这种类型,这个方法最后形成的返回数据为responses["text"]=xhr.responseText,也就是得到服务器端的数据!

(5)ajaxConverter作用:左后返回一个对象,该对象有state和data属性,如{state:"success",data:response}其中response就是上面提到的经过处理的服务器端返回的数据!

(6)如果指定了global表示支持全局事件的调用,那么在jQuery.active的值为0的时候调用一次ajaxStart,调用完成以后让active自增,在调用ajaxStop之前首先让active自减,如果是0才会调用ajaxStop!

上一篇:jQuery 源码分析(十三) 数据操作模块 DOM属性 详解


下一篇:An Exploration of ARM TrustZone Technology