1 //Serialize an array of form elements or a set of
2 //key/values into a query string
3 // 将数组形式的表单元素或者哈希表序列化成字符串
4 jQuery.param = function(a, traditional) {
5 var prefix, s = [],
6 add = function(key, value) {
7 // If value is a function, invoke it and return its value
8 // 如果value是函数就执行并返回执行结果
9 value = jQuery.isFunction(value) ? value() : (value == null ? '' : value);
10 s[s.length] = encodeURIComponent(key) + '=' +
11 encodeURIComponent(value);
12 };
13
14 // Set traditional to true for jQuery <= 1.3.2 behavior
15 if (traditional === undefined) {
16 traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
17 }
18
19 // If an array was passed in, assume that it is an array of form elements.
20 // 如果传进来的是数组,假设是表单元素
21 if (jQuery.isArray(a) || (a.jquery && !jQuery.isPlainObject(a))) {
22 // 序列化表单元素
23 jQuery.each(a, function() {
24 add(this.name, this.value);
25 });
26
27 } else {
28 // If traditional, encode the "old" way (the way 1.3.2 or older
29 // did it), otherwise encode params recursively.
30 for (prefix in a) {
31 buildParams(prefix, a[prefix], traditional, add);
32 }
33 }
34
35 // Return the resulting serialization
36 return s.join('&').replace(r20, '+');
37 };
38
39 function buildParams(prefix, obj, traditional, add) {
40 var name;
41
42 if (jQuery.isArray(obj)) {
43 // Serialize array item.
44 jQuery.each(obj, function(i, v) {
45 if (traditional || rbracket.test(prefix)) {
46 // Treat each array item as a scalar.
47 add(prefix, v);
48
49 } else {
50 // Item is non-scalar (array or object), encode its numeric index
51 buildParams(prefix + '[' + (typeof v === 'object' ? i : '') + ']', v, traditional, add);
52 }
53 });
54
55 } else if (!traditional && jQuery.type(obj) === 'object') {
56 // Serialize object item
57 for (name in obj) {
58 buildParams(prefix + '[' + name + ']', obj[name], traditional, add);
59 }
60
61 } else {
62 // Serialize scalar item
63 add(prefix, obj);
64 }
65 }
66
67
68
69 var
70 // Document location
71 ajaxLocParts,
72 ajaxLocation,
73 ajax_nonce = jQuery.now(),
74
75 // 匹配“?”
76 ajax_rquery = /\?/,
77 // 匹配hash,“#”开头的字符串
78 rhash = /#.*$/,
79 // 匹配“_=”开头到“&”结尾的字符串
80 rts = /([?&])_=[^&]*/,
81 // 匹配头部信息,获取headerName,和headerValue
82 rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg,
83 // IE leaves an \r character at EOL
84 // #7653, #8125, #8152: local protocol detection
85 // 匹配协议, 可以判断是否为本地协议
86 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
87 // 匹配请求是否有内容
88 rnoContent = /^(?:GET|HEAD)$/,
89 // 匹配“//”开头的字符
90 rprotocol = /^\/\//,
91 // 匹配url,例如匹配http://www.baidu.com:8080
92 // 将会获取"http:", "www.baidu.com", "8080"
93 rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
94
95 // Keep a copy of the old load method
96 // jQuery.fn.load旧版本的方法的拷贝
97 _load = jQuery.fn.load,
98
99 /* Prefilters
100 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
101 * 2) These are called:
102 * - BEFORE asking for a transport
103 * - AFTER param serialization (s.data is a string if s.processData is true)
104 * 3) key is the dataType
105 * 4) the catchall symbol "*" can be used
106 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
107 */
108 // 前置过滤器
109 prefilters = {},
110
111 /* Transports bindings
112 * 1) key is the dataType
113 * 2) the catchall symbol "*" can be used
114 * 3) selection will start with transport dataType and THEN go to "*" if needed
115 */
116 // 请求分发器
117 transports = {},
118
119 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
120 // "*/*"
121 allTypes = "*/".concat("*");
122
123 // #8138, IE may throw an exception when accessing
124 // a field from window.location if document.domain has been set
125 // 在IE中,如果document.domain被设置了,获取window.location会报错
126 try {
127 ajaxLocation = location.href;
128 } catch (e) {
129 // Use the href attribute of an A element
130 // since IE will modify it given document.location
131 // 因为IE会修改A标签元素的href属性,添加window.location字符串
132 ajaxLocation = document.createElement('a');
133 ajaxLocation.href = '';
134 ajaxLocation = ajaxLocation.href;
135 }
136
137 // Segment location into parts
138 // [URL, http, host, port]
139 ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || [];
140
141 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
142 // 返回一个函数,为prefilters或者transport添加属性,
143 // 该属性值是一个数组,里面存放的是函数func,
144 // 可以给dataTypeExpression字符串添加标识,表示是添加到数组头部还是尾部
145
146 function addToPrefiltersOrTransports(structure) {
147
148 // dataTypeExpression is optional and defaults to "*"
149 return function(dataTypeExpression, func) {
150 // 如果dataTypeExpression不是字符串,将它赋值给func
151 // 然后把"*"赋值给dataTypeExpression
152 if (typeof dataTypeExpression !== 'string') {
153 func = dataTypeExpression;
154 dataTypeExpression = '*';
155 }
156
157 var dataType, i = 0,
158 // 返回空格分隔的dataTypeExpression数组
159 dataTypes = dataTypeExpression.toLowerCase().match(core_rnotwhite) || [];
160
161 if (jQuery.isFunction(func)) {
162 // For each detaType in the dataTypeExpression
163 // 遍历dataTypes数组
164 while ((dataType = dataTypes[i++])) {
165 // Prepend if requested
166 // 如果第一个字符是“+”,截取“+”后面的字符串或者
167 // 默认为“*”,即匹配所有,
168 // 给structure的属性dataType数组头部添加func
169 if (dataType[0] === '+') {
170 dataType = dataType.slice(1) || '*';
171 (structure[dataType] = structure[dataType] || []).unshift(func);
172
173 // Otherwise append
174 // 否则第一个字符不是“*”就给structure的属性dataType数组
175 // 尾部添加func
176 } else {
177 (structure[dataType] = structure[dataType] || []).push(func);
178 }
179 }
180 }
181 };
182 }
183
184 // Base inspection function for prefilters and transports
185
186 function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) {
187
188 var inspected = {},
189 seekingTransport = (structure === transports);
190
191 // 遍历structure[dataType]数组,并执行回调,
192 // prefilterOrFactory为函数数组元素,
193 // 执行该函数如果返回的结果dataTypeOrTransport是字符串且时prefilters且没有被inspected过,
194 // 就给options.dataTypes数组头部添加该字符串,
195 // 继续递归dataTypeOrTransport(当我们使用json/jsonp的时候会返回“script”,于是会执行“script”相关的回调)。
196 // 如果是transport就返回dataTypeOrTransport的假结果
197
198 function inspect(dataType) {
199 var selected;
200 inspected[dataType] = true;
201 jQuery.each(structure[dataType] || [], function(_, prefilterOrFactory) {
202 var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR);
203 if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) {
204 options.dataTypes.unshift(dataTypeOrTransport);
205 inspect(dataTypeOrTransport);
206 return false;
207 } else if (seekingTransport) {
208 // 如果是查找transport,selected是一个对象,
209 // !selected返回的是false,表示退出each循环
210 return !(selected = dataTypeOrTransport);
211 }
212 });
213 return selected;
214 }
215
216 return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*");
217 }
218
219 // A special extend for ajax options
220 // that takes "flats" options (not to be deep extended)
221 // 对ajax配置项进行扩展
222 // 如果jQuery.ajaxSettings.flatOptions存在src对应的key值,
223 // 就直接给target添加(覆盖)相应key/value,
224 // flatOptions对象里的属性是不需要被深度拷贝的
225 // 否则创建一个deep对象,将src的key/value添加给deep,
226 // 然后深度克隆deep对象到target.
227 // 最后都返回target
228
229 function ajaxExtend(target, src) {
230 var deep, key, flatOptions = jQuery.ajaxSettings.flatOptions || {};
231
232 for (key in src) {
233 if (src[key] !== undefined) {
234 // 如果jQuery.ajaxSettings.flatOptions存在src对应的key值,
235 // 就直接给target添加(覆盖)相应key/value,
236 // 否则创建一个deep对象,将src的key/value添加给deep
237 (flatOptions[key] ? target : (deep || (deep = {})))[key] = src[key];
238 }
239 }
240 // 深度克隆deep对象到target
241 if (deep) {
242 jQuery.extend(true, target, deep);
243 }
244
245 return target;
246 }
247
248 jQuery.fn.load = function(url, params, callback) {
249 // 早期版本的jQuery.fn.load的接口
250 if (typeof url !== 'string' && _load) {
251 return _load.apply(this, arguments);
252 }
253
254 var selector, response, type, self = this,
255 off = url.indexOf(' ');
256
257 // 如果url有空格,空格前面是url,后面是选择器selector
258 if (off >= 0) {
259 selector = url.slice(off, url.length);
260 url = url.slice(0, off);
261 }
262
263 // If it's a function
264 // load(url, function(){})
265 if (jQuery.isFunction(params)) {
266
267 // We assume that it's the callback
268 callback = params;
269 params = undefined;
270
271 // Otherwise, build a param string
272 // 否则如果param是对象,则把ajax类型type设置为“POST”,
273 // 说明是发送数据到服务器
274 } else if (params && typeof params === 'object') {
275 type = 'POST';
276 }
277
278 // If we have elements to modify, make the request
279 // 必须要有元素集
280 if (self.length) {
281 // 调用底层ajax方法, 其中用了Deferred对象
282 jQuery.ajax({
283 url: url,
284
285 // If "type" variable is undefined, then "GET" method will be used
286 type: type,
287 dataType: 'html',
288 data: params
289 }).done(function(responseText) {
290 // 请求成功后
291 // Save response for use in complete callback
292 response = arguments;
293
294 self.html(selector ?
295
296 // If a selector was specified, locate the right elements in a dummy div
297 // Exclude scripts to avoid IE 'Permission Denied' errors
298 // 如果有选择器,则创建一个div节点,
299 // 将返回的数据解析成HTML,然后在该HTML下找到选择器匹配的部分,
300 // 填充到div中
301 jQuery('<div>').append(jQuery.parseHTML(responseText)).find(selector) :
302
303 // Otherwise use the full result
304 // 否则直接填充
305 responseText);
306
307 }).complete(callback && function(jqXHR, status) {
308 // 请求完成后
309 // 遍历每个DOM元素,执行callback, 第二个参数是callback的参数
310 self.each(callback, response || [jqXHR.responseText, status, jqXHR]);
311 });
312 }
313
314 return this;
315 };
316
317 // Attach a bunch of functions for handling common AJAX events
318 // 添加一些AJAX事件方法,这里用的是观察者模式,
319 // jQuery.fn.on方法订阅事件,
320 // jQuery.fn.trigger方法则可以发布事件
321 jQuery.each(["ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend"], function(i, type) {
322 jQuery.fn[type] = function(fn) {
323 return this.on(type, fn);
324 };
325 });
326
327 // 添加jQuery.get和jQuery.post方法, 区别在于ajax的类型是get还是post
328 jQuery.each(['get', 'post'], function(i, method) {
329 jQuery[method] = function(url, data, callback, type) {
330 // shift arguments if data argument was omitted
331 if (jQuery.jsFunction(data)) {
332 type = type || callback;
333 callback = data;
334 data = undefined;
335 }
336
337 return jQuery.ajax({
338 url: url,
339 type: method,
340 dataType: type,
341 data: data,
342 success: callback
343 });
344 };
345 });
346
347 jQuery.extend({
348 // Counter for holding the number of active queries
349 active: 0,
350 // Last-Modified header cache for next request
351 // 上一次被修改的头部的缓存信息
352 lastModified: {},
353 etag: {},
354 // ajax配置项
355 ajaxSettings: {
356 // 个用来包含发送请求的URL字符串。
357 url: ajaxLocation,
358 /**
359 * (默认: "GET") 请求方式 ("POST" 或 "GET"), 默认为 "GET"。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
360 * @type {String}
361 */
362 type: "GET",
363 /**
364 * 是否为本地
365 * 默认: 取决于当前的位置协议
366 * 允许当前环境被认定为“本地”,(如文件系统),即使jQuery默认情况下不会承认它。以下协议目前公认为本地:file, *-extension, and widget。如果isLocal设置需要修改,建议在$.ajaxSetup()方法中这样做一次。
367 * @type {Boolean}
368 */
369 isLocal: rlocalProtocol.test(ajaxLocParts[1]),
370 /**
371 * (默认: true) 是否触发全局 AJAX 事件。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 可用于控制不同的 Ajax 事件。
372 * @type {Boolean}
373 */
374 global: true,
375 /**
376 * (默认: true) 默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
377 * @type {Boolean}
378 */
379 processData: true,
380 /*
381 (默认: true) 默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
382 */
383 async: true,
384 /**
385 * (默认: "application/x-www-form-urlencoded") 发送信息至服务器时内容编码类型。默认值适合大多数情况。如果你明确地传递了一个content-type给 $.ajax() 那么他必定会发送给服务器(即使没有数据要发送)
386 * @type {String}
387 */
388 contentType: "application/x-www-form-urlencoded; charset=UTF-8",
389
390 /*
391 // 设置请求超时时间(毫秒)。此设置将覆盖全局设置。
392 timeout: 0,
393 data: null,
394 dataType: null,
395 // 用于响应HTTP访问认证请求的用户名
396 username: null,
397 // 用于响应HTTP访问认证请求的密码
398 password: null,
399 // (默认: true,dataType为script和jsonp时默认为false),设置为 false 将不缓存此页面。
400 cache: null,
401 throws: false,
402 // 如果你想要用传统的方式来序列化数据,那么就设置为true
403 traditional: false,
404 // 个额外的"{键:值}"对映射到请求一起发送。此设置被设置之前beforeSend函数被调用;因此,消息头中的值设置可以在覆盖beforeSend函数范围内的任何设置。
405 headers: {},
406 */
407 /**
408 * 接受的数据的type类型
409 * 内容类型发送请求头,告诉服务器什么样的响应会接受返回。如果accepts设置需要修改,推荐在$.ajaxSetup()方法中做一次。
410 * @type {Object}
411 */
412 accepts: {
413 "*": allTypes,
414 text: "text/plain",
415 html: "text/html",
416 xml: "application/xml, text/xml",
417 json: "application/json, text/javascript"
418 },
419 /**
420 * 一个以"{字符串:正则表达式}"配对的对象,用来确定jQuery将如何解析响应,给定其内容类型。
421 * @type {Object}
422 */
423 contents: {
424 xml: /xml/,
425 html: /html/,
426 json: /json/
427 },
428 responseFields: {
429 xml: "responseXML",
430 text: "responseText"
431 },
432 // Data converters
433 // Keys separate source (or catchall "*") and destination types with a single space
434 /**
435 * 数据转换器
436 * 一个数据类型对数据类型转换器的对象。每个转换器的值是一个函数,返回响应的转化值
437 * @type {Object}
438 */
439 converters: {
440 // Convert anything to text
441 "* text": window.String,
442 // Text to html (true = no transformation)
443 // true为不转换
444 "text html": true,
445 // Evaluate text as a json expression
446 "text json": jQuery.parseJSON,
447 // Parse text as xml
448 "text xml": jQuery.parseXML
449 },
450 // For options that shouldn't be deep extended:
451 // you can add your own custom options here if
452 // and when you create one that shouldn't be
453 // deep extended (see ajaxExtend)
454 // 不会被深度拷贝的配置项
455 flatOptions: {
456 url: true,
457 context: true
458 }
459 },
460 // Creates a full fledged settings object into target
461 // with both ajaxSettings and settings fields.
462 // If target is omitted, writes into ajaxSettings.
463 // 创建更健壮的配置项到target中,包含了ajaxSettings和settings参数
464 // 如果只有一个参数,直接添加到jQuery.ajaxSettings中
465 ajaxSetup: function(target, settings) {
466 return settings ?
467 // Building a settings object
468 ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) :
469 // Extending ajaxSettings
470 ajaxExtend(jQuery.ajaxSettings, target);
471 },
472
473 ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
474 ajaxTransport: addToPrefiltersOrTransports(transports),
475
476 // Main method
477 ajax: function(url, options) {
478 // If url is an object, simulate pre-1.5 signature
479 if (typeof url === "object") {
480 options = url;
481 url = undefined;
482 }
483
484 // Force options to be an object
485 options = options || {};
486
487 var // Cross-domain detection vars
488 // 跨域检测变量
489 parts,
490 // Loop variable
491 i,
492 // URL without anti-cache param
493 // 没有破坏缓存参数的url,即不会
494 cacheURL,
495 // Response headers as string
496 responseHeadersString,
497 // timeout handle
498 // 计时器
499 timeoutTimer,
500
501 // To know if global events are to be dispatched
502 // 全局事件是否该被触发
503 fireGlobals,
504
505 transport,
506 // Response headers
507 responseHeaders,
508 // Create the final options object
509 // 最终的ajax配置项
510 s = jQuery.ajaxSetup({}, options),
511 // Callbacks context
512 // 回调函数的上下文
513 callbackContext = s.context || s,
514 // Context for global events is callbackContext if it is a DOM node or jQuery collection
515 // 如果配置项有context则用jQuery对象,否则使用jQuery.event对象
516 globalEventContext = s.context && (callbackContext.nodeType || callbackContext.jquery) ? jQuery(callbackContext) : jQuery.event,
517 // Deferreds
518 // 创建一个延迟对象
519 deferred = jQuery.Deferred(),
520 // 完成延迟的回调列表
521 completeDeferred = jQuery.Callbacks("once memory"),
522 // Status-dependent callbacks
523 // 状态码, 根据status来决定回调
524 statusCode = s.statusCode || {},
525 // Headers (they are sent all at once)
526 requestHeaders = {},
527 requestHeadersNames = {},
528 // The jqXHR state
529 // jqXHR的状态
530 state = 0,
531 // Default abort message
532 // 默认退出信息
533 strAbort = "canceled",
534 // Fake xhr
535 // 伪装的xhr对象
536 jqXHR = {
537 readyState: 0,
538 // Builds headers hashtable if needed
539 // 如果需要就创建头部哈希表
540 getResponseHeader: function(key) {
541 var match;
542 // 如果jqXHR的状态为2
543 if (state === 2) {
544 if (!responseHeaders) {
545 responseHeaders = {};
546 // 逐个获取已有的头部信息responseHeadersString的key和value值,
547 // 给responseHeaders对象添加key属性和相应的value
548 while ((match = rheaders.exec(responseHeadersString))) {
549 responseHeaders[match[1].toLowerCase()] = match[2];
550 }
551 }
552 // 给responseHeaders添加参数key的属性
553 match = responseHeaders[key.toLowerCase()];
554 }
555 // 返回responseHeaders
556 return match == null ? null : match;
557 },
558
559 // Raw string
560 getAllResponseHeaders: function() {
561 return state === 2 ? responseHeadersString : null;
562 },
563
564 // Caches the header
565 // 缓存头部
566 setRequestHeader: function(name, value) {
567 var lname = name.toLowerCase();
568 // 如果jqXHR的state小于等于0,
569 // 获取requestHeadersNames的name属性值(没有该属性就添加),
570 // 添加requestHeaders的name属性和对应的value值
571 if (!state) {
572 name = requestHeadersNames[lname] = requestHeadersNames[lname] || name;
573 requestHeaders[name] = value;
574 }
575 return this;
576 },
577
578 // Overrides response content-type header
579 // 重写响应的mimeType
580 overrideMimeType: function(type) {
581 if (!state) {
582 s.mimeType = type;
583 }
584 return this;
585 },
586
587 // Status-dependent callbacks
588 statusCode: function(map) {
589 var code;
590 // 如果存在map对象
591 if (map) {
592 // 如果jqXHR的状态小于2,
593 // 说明没有完成
594 if (state < 2) {
595 // 遍历map
596 for (code in map) {
597 // Lazy-add the new callback in a way that preserves old ones
598 // 给statusCode对象添加code属性,值为数组,
599 // 第一个元素存放着旧的值
600 statusCode[code] = [statusCode[code], map[code]];
601 }
602
603 // 如果jqXHR大于等于2
604 } else {
605 // Execute the appropriate callbacks
606 // 执行相应的回调
607 jqXHR.always(map[jqXHR.status]);
608 }
609 }
610 return this;
611 },
612 // Cancel the request
613 // 取消请求
614 abort: function(statusText) {
615 var finalText = statusText || strAbort;
616 if (transport) {
617 transport.abort(finalText);
618 }
619 done(0, finalText);
620 return this;
621 }
622 };
623
624 // Attach deferreds
625 // 给jqXHR添加promise的属性和方法,
626 // 然后添加complete方法,这里用的是回调列表的add方法(即添加回调)
627 // 订阅完成回调
628 deferred.promise(jqXHR).complete = completeDeferred.add;
629 // success/error 方法则是使用promise的done/fail方法
630 jqXHR.success = jqXHR.done;
631 jqXHR.error = jqXHR.fail;
632
633 // Remove hash character (#7531: and string promotion)
634 // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
635 // Handle falsy url in the settings object (#10093: consistency with old signature)
636 // We also use the url parameter if available
637 s.url = ((url || s.url || ajaxLocation) + "").replace(rhash, "").replace(rprotocol, ajaxLocParts[1] + "//");
638
639 // Alias method option to type as per ticket #12004
640 s.type = options.method || options.type || s.method || s.type;
641
642 // Extract dataTypes list
643 // dataTypes列表
644 s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(core_rnotwhite) || [""];
645
646 // A cross-domain request is in order when we have a protocol:host:port mismatch
647 // 检测是否需要跨域请求
648 if (s.crossDomain == null) {
649 parts = rurl.exec(s.url.toLowerCase());
650 s.crossDomain = !! (parts && (parts[1] !== ajaxLocParts[1] || parts[2] !== ajaxLocParts[2] || (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? 80 : 443))));
651 }
652
653 // Convert data if not already a string
654 // 如果data不是字符串则转换为字符串形式“a=1&b=2&c=3”
655 if (s.data && s.processData && typeof s.data !== "string") {
656 s.data = jQuery.param(s.data, s.traditional);
657 }
658
659 // Apply prefilters
660 // 遍历prefilters[dataType]数组,并执行回调,
661 // prefilterOrFactory为函数数组元素,
662 // 执行该函数如果返回的结果dataTypeOrTransport是字符串且时prefilters且没有被inspected过,
663 // 就给options.dataTypes数组头部添加该字符串,
664 // 继续递归dataTypeOrTransport(当我们使用json/jsonp的时候会返回“script”,于是会执行“script”相关的回调)。
665 inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);
666
667 // If request was aborted inside a prefilter, stop there
668 if (state === 2) {
669 return jqXHR;
670 }
671
672 // We can fire global events as of now if asked to
673 // 是否触发全局事件
674 fireGlobals = s.global;
675
676 // Watch for a new set of requests
677 if (fireGlobals && jQuery.active++ === 0) {
678 jQuery.event.trigger("ajaxStart");
679 }
680
681 // Uppercase the type
682 // 将type转换为大写
683 s.type = s.type.toUpperCase();
684
685 // Determine if request has content
686 // 请求是否有内容
687 s.hasContent = !rnoContent.test(s.type);
688
689 // Save the URL in case we're toying with the If-Modified-Since
690 // and/or If-None-Match header later on
691 // 保存url
692 cacheURL = s.url;
693
694 // More options handling for requests with no content
695 if (!s.hasContent) {
696
697 // If data is available, append data to url
698 // 如果有data,将data字符串加入到url
699 if (s.data) {
700 cacheURL = (s.url += (ajax_rquery.test(cacheURL) ? "&" : "?") + s.data);
701 // #9682: remove data so that it's not used in an eventual retry
702 // 删除data属性,防止被重用
703 delete s.data;
704 }
705
706 // Add anti-cache in url if needed
707 // 如果不需要缓存,则添加破坏缓存时间戳
708 if (s.cache === false) {
709 s.url = rts.test(cacheURL) ?
710 // If there is already a '_' parameter, set its value
711 // 如果已经存在_字段,则修改它的值
712 cacheURL.replace(rts, "$1_=" + ajax_nonce++) :
713 // Otherwise add one to the end
714 // 否则就在尾部添加
715 cacheURL + (ajax_rquery.test(cacheURL) ? "&" : "?") + "_=" + ajax_nonce++;
716 }
717 }
718
719 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
720 /*
721 (默认: false) 仅在服务器数据改变时获取新数据。使用 HTTP 包 Last-Modified 头信息判断。他也会检查服务器指定的'etag'来确定数据没有被修改过。
722 */
723 if (s.ifModified) {
724 if (jQuery.lastModified[cacheURL]) {
725 jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[cacheURL]);
726 }
727 if (jQuery.etag[cacheURL]) {
728 jqXHR.setRequestHeader("If-None-Match", jQuery.etag[cacheURL]);
729 }
730 }
731
732 // Set the correct header, if data is being sent
733 // 设置正确的头信息
734 if (s.data && s.hasContent && s.contentType !== false || options.contentType) {
735 jqXHR.setRequestHeader("Content-Type", s.contentType);
736 }
737
738 // Set the Accepts header for the server, depending on the dataType
739 // 为服务端添加Accepts头,取决于dataType,
740 // 例如dataType为“html”,则value值为"text/html, */*; q=0.01", s.accepts['html'],
741 // 即"text/html"
742 // 没有dataType就用“*/*”
743 jqXHR.setRequestHeader("Accept", s.dataTypes[0] && s.accepts[s.dataTypes[0]] ? s.accepts[s.dataTypes[0]] + (s.dataTypes[0] !== "*" ? ", " + allTypes + "; q=0.01" : "") : s.accepts["*"]);
744
745 // Check for headers option
746 // 添加配置项headers的头部请求
747 for (i in s.headers) {
748 jqXHR.setRequestHeader(i, s.headers[i]);
749 }
750
751 // Allow custom headers/mimetypes and early abort
752 // 执行beforeSend方法,如果该函数返回false则推出
753 if (s.beforeSend && (s.beforeSend.call(callbackContext, jqXHR, s) === false || state === 2)) {
754 // Abort if not done already and return
755 // canceled
756 return jqXHR.abort();
757 }
758
759 // aborting is no longer a cancellation
760 strAbort = "abort";
761
762 // Install callbacks on deferreds
763 // 给回调列表添加回调
764 for (i in {
765 success: 1,
766 error: 1,
767 complete: 1
768 }) {
769 jqXHR[i](s[i]);
770 }
771
772 // Get transport
773 transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
774
775 // If no transport, we auto-abort
776 if (!transport) {
777 done(-1, "No Transport");
778 } else {
779 // 请求开始
780 jqXHR.readyState = 1;
781
782 // Send global event
783 // 发送全局事件ajaxSend
784 if (fireGlobals) {
785 globalEventContext.trigger("ajaxSend", [jqXHR, s]);
786 }
787 // Timeout
788 // 如果是异步且配置项中timeout有值,
789 // 则设置定时器, 当定时期结束时就会执行abort方法
790 if (s.async && s.timeout > 0) {
791 timeoutTimer = setTimeout(function() {
792 jqXHR.abort("timeout");
793 }, s.timeout);
794 }
795
796 try {
797 // 设置jqXHR的状态为1
798 state = 1;
799 // 创建并发送请求,这里才开始调用原生方法
800 transport.send(requestHeaders, done);
801 } catch (e) {
802 // Propagate exception as error if not done
803 // 如果未完成则当错误处理
804 if (state < 2) {
805 done(-1, e);
806 // Simply rethrow otherwise
807 } else {
808 throw e;
809 }
810 }
811 }
812
813 // Callback for when everything is done
814
815 function done(status, nativeStatusText, responses, headers) {
816 var isSuccess, success, error, response, modified, statusText = nativeStatusText;
817
818 // Called once
819 // 只执行一次
820 if (state === 2) {
821 return;
822 }
823
824 // State is "done" now
825 // jqXHR的状态设置为2
826 state = 2;
827
828 // Clear timeout if it exists
829 // 清除定时器
830 if (timeoutTimer) {
831 clearTimeout(timeoutTimer);
832 }
833
834 // Dereference transport for early garbage collection
835 // (no matter how long the jqXHR object will be used)
836 // 取消引用
837 transport = undefined;
838
839 // Cache response headers
840 // 缓存响应头
841 responseHeadersString = headers || "";
842
843 // Set readyState
844 // 设置readyState, 根据state来判断
845 jqXHR.readyState = status > 0 ? 4 : 0;
846
847 // Get response data
848 // 获取响应的数据, 这里会使用ajaxHandleResponses来处理响应内容
849 if (responses) {
850 response = ajaxHandleResponses(s, jqXHR, responses);
851 }
852
853 // If successful, handle type chaining
854 // 请求成功时
855 if (status >= 200 && status < 300 || status === 304) {
856
857 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
858 // 如果处于ifModified模式, 设置If-Modified-Since和/或者If-None-Match header
859 if (s.ifModified) {
860 modified = jqXHR.getResponseHeader("Last-Modified");
861 if (modified) {
862 jQuery.lastModified[cacheURL] = modified;
863 }
864 modified = jqXHR.getResponseHeader("etag");
865 if (modified) {
866 jQuery.etag[cacheURL] = modified;
867 }
868 }
869
870 // if no content
871 // 如果没有内容
872 if (status === 204) {
873 isSuccess = true;
874 statusText = "nocontent";
875
876 // if not modified
877 // 如果没有被修改
878 } else if (status === 304) {
879 isSuccess = true;
880 statusText = "notmodified";
881
882 // If we have data, let's convert it
883 // 如果有数据就转换它
884 } else {
885 isSuccess = ajaxConvert(s, response);
886 statusText = isSuccess.state;
887 success = isSuccess.data;
888 error = isSuccess.error;
889 isSuccess = !error;
890 }
891
892 // 请求失败时
893 } else {
894 // We extract error from statusText
895 // then normalize statusText and status for non-aborts
896 error = statusText;
897 if (status || !statusText) {
898 statusText = "error";
899 if (status < 0) {
900 status = 0;
901 }
902 }
903 }
904
905 // Set data for the fake xhr object
906 // 给伪装的jqXHR附加属性
907 jqXHR.status = status;
908 jqXHR.statusText = (nativeStatusText || statusText) + "";
909
910 // Success/Error
911 // 触发成功/失败回调
912 if (isSuccess) {
913 deferred.resolveWith(callbackContext, [success, statusText, jqXHR]);
914 } else {
915 deferred.rejectWith(callbackContext, [jqXHR, statusText, error]);
916 }
917
918 // Status-dependent callbacks
919 // 根据statusCode对应的状态码属性触发相应的回调
920 jqXHR.statusCode(statusCode);
921 statusCode = undefined;
922
923 if (fireGlobals) {
924 // 触发全局ajaxSuccess方法
925 globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError", [jqXHR, s, isSuccess ? success : error]);
926 }
927
928 // Complete
929 // 触发complete方法
930 completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);
931
932 if (fireGlobals) {
933 // 触发全局ajaxComplete方法
934 globalEventContext.trigger("ajaxComplete", [jqXHR, s]);
935 // Handle the global AJAX counter
936 // 如果active<=0,触发ajaxStop方法
937 if (!(--jQuery.active)) {
938 jQuery.event.trigger("ajaxStop");
939 }
940 }
941 }
942
943 return jqXHR;
944 },
945 getScript: function(url, callback) {
946 return jQuery.get(url, undefined, callback, "script");
947 },
948 getJSON: function(url, data, callback) {
949 return jQuery.get(url, data, callback, "json");
950 }
951 });
952
953 /* Handles responses to an ajax request:
954 * - sets all responseXXX fields accordingly
955 * - finds the right dataType (mediates between content-type and expected dataType)
956 * - returns the corresponding response
957 */
958 // 给s.dataTypes头部添加“*”或"text",给ajaxConvert使用
959
960 function ajaxHandleResponses(s, jqXHR, responses) {
961 var firstDataType, ct, finalDataType, type,
962 // /xml/ | /html/ | /json/
963 contents = s.contents,
964 dataTypes = s.dataTypes,
965 // responseXML|responseText
966 responseFields = s.responseFields;
967
968 // Fill responseXXX fields
969 // 给jqXHR填充responseXML/responseText
970 for (type in responseFields) {
971 if (type in responses) {
972 jqXHR[responseFields[type]] = responses[type];
973 }
974 }
975
976 // Remove auto dataType and get content-type in the process
977 // 遍历删除“*”开头的dataTypes数组元素,然后获取content-type
978 while (dataTypes[0] === "*") {
979 dataTypes.shift();
980 if (ct === undefined) {
981 ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
982 }
983 }
984
985 // Check if we're dealing with a known content-type
986 // 检查我们正在处理的是否是一致的content-type
987 // 如果是就给dataTypes数组的头部添加type
988 /**
989 * contents: {
990 xml: /xml/,
991 html: /html/,
992 json: /json/
993 }
994 */
995 if (ct) {
996 for (type in contents) {
997 if (contents[type] && contents[type].test(ct)) {
998 dataTypes.unshift(type);
999 break;
1000 }
1001 }
1002 }
1003
1004 // Check to see if we have a response for the expected dataType
1005 // 检查我们的响应是否是预期的dataType
1006 if (dataTypes[0] in responses) {
1007 finalDataType = dataTypes[0];
1008
1009 // 如果不是, 尝试转换dataTypes
1010 } else {
1011 // Try convertible dataTypes
1012 for (type in responses) {
1013 // 如果此时dataTypes数组第一个元素没有值或者可以匹配到converters,
1014 // 取得最终的finalDataType,结束循环
1015 if (!dataTypes[0] || s.converters[type + " " + dataTypes[0]]) {
1016 finalDataType = type;
1017 break;
1018 }
1019 if (!firstDataType) {
1020 firstDataType = type;
1021 }
1022 }
1023 // Or just use first one
1024 // text | *
1025 finalDataType = finalDataType || firstDataType;
1026 }
1027
1028 // If we found a dataType
1029 // We add the dataType to the list if needed
1030 // and return the corresponding response
1031 // 如果finalDataType不是dataTypes中的第一个元素,
1032 // 我们将它添加到第一个,
1033 // 返回responses[finalDataType]
1034 if (finalDataType) {
1035 if (finalDataType !== dataTypes[0]) {
1036 // 给dataTypes数组的第一个元素添加"text"或"*"
1037 dataTypes.unshift(finalDataType);
1038 }
1039 return responses[finalDataType];
1040 }
1041 }
1042
1043 // Chain conversions given the request and the original response
1044 // 转换响应的数据
1045
1046 function ajaxConvert(s, response) {
1047 var conv2, current, conv, tmp, converters = {},
1048 i = 0,
1049 // Work with a copy of dataTypes in case we need to modify it for conversion
1050 dataTypes = s.dataTypes.slice(),
1051 // "*" |"text"
1052 prev = dataTypes[0];
1053
1054 // Apply the dataFilter if provided
1055 /**
1056 * 给Ajax返回的原始数据的进行预处理的函数。提供data和type两个参数:data是Ajax返回的原始数据,type是调用jQuery.ajax时提供的dataType参数。函数返回的值将由jQuery进一步处理。
1057 */
1058 if (s.dataFilter) {
1059 response = s.dataFilter(response, s.dataType);
1060 }
1061
1062 // Create converters map with lowercased keys
1063 if (dataTypes[1]) {
1064 for (conv in s.converters) {
1065 converters[conv.toLowerCase()] = s.converters[conv];
1066 }
1067 }
1068
1069 // Convert to each sequential dataType, tolerating list modification
1070 for (;
1071 (current = dataTypes[++i]);) {
1072
1073 // There's only work to do if current dataType is non-auto
1074 if (current !== "*") {
1075
1076 // Convert response if prev dataType is non-auto and differs from current
1077 if (prev !== "*" && prev !== current) {
1078
1079 // Seek a direct converter
1080 conv = converters[prev + " " + current] || converters["* " + current];
1081
1082 // If none found, seek a pair
1083 if (!conv) {
1084 for (conv2 in converters) {
1085
1086 // If conv2 outputs current
1087 tmp = conv2.split(" ");
1088 if (tmp[1] === current) {
1089
1090 // If prev can be converted to accepted input
1091 conv = converters[prev + " " + tmp[0]] || converters["* " + tmp[0]];
1092 if (conv) {
1093 // Condense equivalence converters
1094 if (conv === true) {
1095 conv = converters[conv2];
1096
1097 // Otherwise, insert the intermediate dataType
1098 } else if (converters[conv2] !== true) {
1099 current = tmp[0];
1100 dataTypes.splice(i--, 0, current);
1101 }
1102
1103 break;
1104 }
1105 }
1106 }
1107 }
1108
1109 // Apply converter (if not an equivalence)
1110 if (conv !== true) {
1111
1112 // Unless errors are allowed to bubble, catch and return them
1113 if (conv && s["throws"]) {
1114 response = conv(response);
1115 } else {
1116 try {
1117 response = conv(response);
1118 } catch (e) {
1119 return {
1120 state: "parsererror",
1121 error: conv ? e : "No conversion from " + prev + " to " + current
1122 };
1123 }
1124 }
1125 }
1126 }
1127
1128 // Update prev for next iteration
1129 prev = current;
1130 }
1131 }
1132
1133 return {
1134 state: "success",
1135 data: response
1136 };
1137 }
1138
1139 // Install script dataType
1140 // 给jQuery.ajaxSettings添加相应的数据(深度拷贝)
1141 jQuery.ajaxSetup({
1142 accepts: {
1143 script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
1144 },
1145 contents: {
1146 script: /(?:java|ecma)script/
1147 },
1148 converters: {
1149 "text script": function(text) {
1150 jQuery.globalEval(text);
1151 return text;
1152 }
1153 }
1154 });
1155
1156 // Handle cache's special case and global
1157 // 给prefilters['script']数组尾部添加回调,
1158 jQuery.ajaxPrefilter("script", function(s) {
1159 if (s.cache === undefined) {
1160 s.cache = false;
1161 }
1162 // 跨域请求限制
1163 if (s.crossDomain) {
1164 s.type = "GET";
1165 s.global = false;
1166 }
1167 });
1168
1169 // Bind script tag hack transport
1170 // 给transports['script']数组尾部添加回调
1171 jQuery.ajaxTransport("script", function(s) {
1172
1173 // This transport only deals with cross domain requests
1174 // 该分发器只处理跨域请求
1175 if (s.crossDomain) {
1176
1177 var script,
1178 head = document.head || jQuery("head")[0] || document.documentElement;
1179
1180 return {
1181
1182 send: function(_, callback) {
1183 // 创建一个script标签,添加相应属性
1184 script = document.createElement('script');
1185
1186 script.async = true;
1187
1188 if (s.scriptCharset) {
1189 script.charset = s.scriptCharset;
1190 }
1191
1192 script.src = s.url;
1193
1194 // Attach handlers for all browsers
1195 // 为所有浏览器添加事件处理器
1196 script.onload = script.onreadystatechange = function(_, isAbort) {
1197 // 如果script加载完毕或者isAbort为true
1198 if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
1199
1200 // Handle memory leak in IE
1201 // 处理IE的内存泄露
1202 script.onload = script.onreadystatechange = null;
1203
1204 // Remove the script
1205 // 删除script标签
1206 if (script.parentNode) {
1207 script.parentNode.removeChild(script);
1208 }
1209
1210 // Dereference the script
1211 script = null;
1212
1213 // Callback if not abort
1214 // 如果isAbort部位true执行callback
1215 if (!isAbort) {
1216 callback(200, "success");
1217 }
1218 }
1219 };
1220
1221 // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
1222 // Use native DOM manipulation to avoid our domManip AJAX trickery
1223 // 插入到头部避免IE6的bug
1224 head.insertBefore(script, head.firstChild);
1225 },
1226 // 退出的时候执行的回调
1227 abort: function() {
1228 if (script) {
1229 script.onload(undefined, true);
1230 }
1231 }
1232 };
1233 }
1234 });
1235 var oldCallbacks = [],
1236 // 匹配=?或者??
1237 rjsonp = /(=)\?(?=&|$)|\?\?/;
1238
1239 // Default jsonp settings
1240 // 为jQuery.ajaxSettings添加jsonp属性和jsonpCallback方法
1241 jQuery.ajaxSetup({
1242 jsonp: "callback",
1243 jsonpCallback: function() {
1244 // TODO
1245 var callback = oldCallbacks.pop() || (jQuery.expando + "_" + (ajax_nonce++));
1246 this[callback] = true;
1247 return callback;
1248 }
1249 });
1250
1251 // Detect, normalize options and install callbacks for jsonp requests
1252 // 给prefilters['json']和prefilters['jsonp']数组添加回调
1253 jQuery.ajaxPrefilter("json jsonp", function(s, originalSettings, jqXHR) {
1254
1255 var callbackName, overwritten, responseContainer,
1256 /*
1257 在一个jsonp请求中重写回调函数的名字。这个值用来替代在"callback=?"这种GET或POST请求中URL参数里的"callback"部分,比如{jsonp:'onJsonPLoad'}会导致将"onJsonPLoad=?"传给服务器。
1258 */
1259 // 如果url中包含了=?或者??,用“url”,否则如果data中有就用"data"
1260 jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ?
1261 "url" :
1262 typeof s.data === "string" && !(s.contentType || "").indexOf("application/x-www-form-urlencoded") && rjsonp.test(s.data) && "data"
1263 );
1264
1265 // Handle if the expected data type is "jsonp" or we have a parameter to set
1266 // 如果有jsonProp或者第一个data-type是"jsonp"
1267 if (jsonProp || s.dataTypes[0] === "jsonp") {
1268
1269 // Get callback name, remembering preexisting value associated with it
1270 // 获取回调名称,如果是函数就将函数返回值当做名称
1271 callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ?
1272 s.jsonpCallback() :
1273 s.jsonpCallback;
1274
1275 // Insert callback into url or form data
1276 // 如果有jsonProp,将s[jsonProp]的=?替换成=callbackName
1277 if (jsonProp) {
1278 s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);
1279 } else if (s.jsonp !== false) {
1280 // 否则如果s.jsonp为true,直接在url上面添加
1281 s.url += (ajax_rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
1282 }
1283
1284 // Use data converter to retrieve json after script execution
1285 // 当script执行后使用converter取回json对象
1286 s.converters["script json"] = function() {
1287 if (!responseContainer) {
1288 jQuery.error(callbackName + " was not called");
1289 }
1290 return responseContainer[0];
1291 };
1292
1293 // force json dataType
1294 // 强制将dataType数组第一个元素设为“json”
1295 s.dataTypes[0] = "json";
1296
1297 // Install callback
1298 // 安装回调
1299 // 先用个变量保存以前callback的内容
1300 overwritten = window[callbackName];
1301 window[callbackName] = function() {
1302 // responseContainer为参数,其中第一个参数是服务端返回的json对象
1303 responseContainer = arguments;
1304 };
1305
1306 // Clean-up function (fires after converters)
1307 jqXHR.always(function() {
1308 // Restore preexisting value
1309 window[callbackName] = overwritten;
1310
1311 // Save back as free
1312 if (s[callbackName]) {
1313 // make sure that re-using the options doesn't screw things around
1314 s.jsonpCallback = originalSettings.jsonpCallback;
1315
1316 // save the callback name for future use
1317 oldCallbacks.push(callbackName);
1318 }
1319
1320 // Call if it was a function and we have a response
1321 if (responseContainer && jQuery.isFunction(overwritten)) {
1322 overwritten(responseContainer[0]);
1323 }
1324
1325 responseContainer = overwritten = undefined;
1326 });
1327
1328 // Delegate to script
1329 return "script";
1330 }
1331 });
1332 var xhrCallbacks, xhrSupported,
1333 xhrId = 0,
1334 // #5280: Internet Explorer will keep connections alive if we don't abort on unload
1335 xhrOnUnloadAbort = window.ActiveXObject && function() {
1336 // Abort all pending requests
1337 var key;
1338 for (key in xhrCallbacks) {
1339 xhrCallbacks[key](undefined, true);
1340 }
1341 };
1342
1343 // Functions to create xhrs
1344 // 创建标准的XHR对象
1345
1346 function createStandardXHR() {
1347 try {
1348 return new window.XMLHttpRequest();
1349 } catch (e) {}
1350 }
1351
1352 // 创建IE的XHR对象
1353
1354 function createActiveXHR() {
1355 try {
1356 return new window.ActiveXObject("Microsoft.XMLHTTP");
1357 } catch (e) {}
1358 }
1359
1360 // Create the request object
1361 // (This is still attached to ajaxSettings for backward compatibility)
1362 // 如果支持ActiveXObject对象,先试着创建标准的XHR,不行就使用ActiveXObject,
1363 // 否则就使用标准的xhr对象
1364 jQuery.ajaxSettings.xhr = window.ActiveXObject ?
1365 /* Microsoft failed to properly
1366 * implement the XMLHttpRequest in IE7 (can't request local files),
1367 * so we use the ActiveXObject when it is available
1368 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
1369 * we need a fallback.
1370 */
1371
1372 function() {
1373 return !this.isLocal && createStandardXHR() || createActiveXHR();
1374 } :
1375 // For all other browsers, use the standard XMLHttpRequest object
1376 createStandardXHR;
1377
1378 // Determine support properties
1379 // 检测浏览器是否支持ajax
1380 xhrSupported = jQuery.ajaxSettings.xhr();
1381 jQuery.support.cors = !! xhrSupported && ("withCredentials" in xhrSupported);
1382 xhrSupported = jQuery.support.ajax = !! xhrSupported;
1383
1384 // Create transport if the browser can provide an xhr
1385 if (xhrSupported) {
1386 // 给 transports['*']数组添加回调
1387 jQuery.ajaxTransport(function(s) {
1388 // Cross domain only allowed if supported through XMLHttpRequest
1389 // 不进行跨域请求或者只有支持XMLHttpRequest跨域请求的才符合条件
1390 if (!s.crossDomain || jQuery.support.cors) {
1391
1392 var callback;
1393
1394 return {
1395 send: function(headers, complete) {
1396
1397 // Get a new xhr
1398 var handle, i,
1399 // 创建一个xhr的实例
1400 xhr = s.xhr();
1401
1402 // Open the socket
1403 // Passing null username, generates a login popup on Opera (#2865)
1404 // 如果有username就传username和password
1405 if (s.username) {
1406 xhr.open(s.type, s.url, s.async, s.username, s.password);
1407 } else {
1408 // 否则直接传
1409 xhr.open(s.type, s.url, s.async);
1410 }
1411
1412 // Apply custom fields if provided
1413 /*
1414 对“文件名-文件值”在本机设置XHR对象。例如,如果需要的话,你可以用它来设置withCredentials为true的跨域请求。
1415 */
1416 if (s.xhrFields) {
1417 for (i in s.xhrFields) {
1418 xhr[i] = s.xhrFields[i];
1419 }
1420 }
1421
1422 // Override mime type if needed
1423 // 重写mimeType
1424 if (s.mimeType && xhr.overrideMimeType) {
1425 xhr.overrideMimeType(s.mimeType);
1426 }
1427
1428 // X-Requested-With header
1429 // For cross-domain requests, seeing as conditions for a preflight are
1430 // akin to a jigsaw puzzle, we simply never set it to be sure.
1431 // (it can always be set on a per-request basis or even using ajaxSetup)
1432 // For same-domain requests, won't change header if already provided.
1433 // 如果没有跨域且头没有"X-Requested-With"属性,添加
1434 if (!s.crossDomain && !headers["X-Requested-With"]) {
1435 headers["X-Requested-With"] = "XMLHttpRequest";
1436 }
1437
1438 // Need an extra try/catch for cross domain requests in Firefox 3
1439 // 需要为FF3捕获错误
1440 try {
1441 for (i in headers) {
1442 xhr.setRequestHeader(i, headers[i]);
1443 }
1444 } catch (err) {}
1445
1446 // Do send the request
1447 // This may raise an exception which is actually
1448 // handled in jQuery.ajax (so no try/catch here)
1449 // 真正的发送请求了
1450 xhr.send((s.hasContent && s.data) || null);
1451
1452 // Listener
1453 callback = function(_, isAbort) {
1454 var status, responseHeaders, statusText, responses;
1455
1456 // Firefox throws exceptions when accessing properties
1457 // of an xhr when a network error occurred
1458 // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
1459 // 当网络发生错误的时候,FF会报错
1460 try {
1461
1462 // Was never called and is aborted or complete
1463 // callback从未被执行且(需要退出请求或者已经请求完毕)
1464 if (callback && (isAbort || xhr.readyState === 4)) {
1465
1466 // Only called once
1467 // 重写自己确保只执行一次
1468 callback = undefined;
1469
1470 // Do not keep as active anymore
1471 // 这里的handle时xhrId,
1472 // 我们需要注销有关信息
1473 if (handle) {
1474 xhr.onreadystatechange = jQuery.noop;
1475 if (xhrOnUnloadAbort) {
1476 delete xhrCallbacks[handle];
1477 }
1478 }
1479
1480 // If it's an abort
1481 // 如果需要退出请求,当请求还没执行完毕时,执行abort方法
1482 if (isAbort) {
1483 // Abort it manually if needed
1484 if (xhr.readyState !== 4) {
1485 xhr.abort();
1486 }
1487 } else {
1488 responses = {};
1489 status = xhr.status;
1490 responseHeaders = xhr.getAllResponseHeaders();
1491
1492 // When requesting binary data, IE6-9 will throw an exception
1493 // on any attempt to access responseText (#11426)
1494 if (typeof xhr.responseText === "string") {
1495 responses.text = xhr.responseText;
1496 }
1497
1498 // Firefox throws an exception when accessing
1499 // statusText for faulty cross-domain requests
1500 try {
1501 statusText = xhr.statusText;
1502 } catch (e) {
1503 // We normalize with Webkit giving an empty statusText
1504 statusText = "";
1505 }
1506
1507 // Filter status for non standard behaviors
1508
1509 // If the request is local and we have data: assume a success
1510 // (success with no data won't get notified, that's the best we
1511 // can do given current implementations)
1512 if (!status && s.isLocal && !s.crossDomain) {
1513 status = responses.text ? 200 : 404;
1514 // IE - #1450: sometimes returns 1223 when it should be 204
1515 } else if (status === 1223) {
1516 status = 204;
1517 }
1518 }
1519 }
1520 } catch (firefoxAccessException) {
1521 if (!isAbort) {
1522 complete(-1, firefoxAccessException);
1523 }
1524 }
1525
1526 // Call complete if needed
1527 if (responses) {
1528 complete(status, statusText, responses, responseHeaders);
1529 }
1530 };
1531
1532 if (!s.async) {
1533 // if we're not in sync mode we fire the callback
1534 // 如果是同步请求,我们立刻执行回调
1535 callback();
1536 } else if (xhr.readyState === 4) {
1537 // (IE6 & IE7) if it's in cache and has been
1538 // retrieved directly we need to fire the callback
1539 // 如果请求成功后直接执行callback
1540 setTimeout(callback);
1541 } else {
1542 handle = ++xhrId;
1543 // IE会保持连接,只有在unload事件中退出请求
1544 if (xhrOnUnloadAbort) {
1545 // Create the active xhrs callbacks list if needed
1546 // and attach the unload handler
1547 if (!xhrCallbacks) {
1548 xhrCallbacks = {};
1549 jQuery(window).unload(xhrOnUnloadAbort);
1550 }
1551 // Add to list of active xhrs callbacks
1552 // 给激活的xhr列表添加回调
1553 xhrCallbacks[handle] = callback;
1554 }
1555 xhr.onreadystatechange = callback;
1556 }
1557 },
1558
1559 abort: function() {
1560 if (callback) {
1561 callback(undefined, true);
1562 }
1563 }
1564 };
1565 }
1566 });
1567 }