jQuery1.9.1源码分析--Ajax模块

   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

 

posted @ 2013-09-24 21:02  LukeLin  阅读(693)  评论(0编辑  收藏  举报