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 }