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

 

你可能感兴趣的:(jquery)