Skip to main content
 首页 » 编程设计

jQuery源码分析之Ajax模块

2022年07月16日51yxwkf
   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     }
View Code

本文参考链接:https://www.cnblogs.com/webFrontDev/p/3337642.html