zoukankan      html  css  js  c++  java
  • 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 = /^(.*?):[ 	]*([^
    ]*)
    ?$/mg,
      83         // IE leaves an 
     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
  • 相关阅读:
    分享50个漂亮的设计师个人作品集网站案例
    Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
    ajax异步请求
    angularJS(6)
    angularJS(5)
    PHP实现RTX发送消息提醒
    ajax异步请求
    angularJS(4)
    angularJS(3)
    jsPanel插件Option总结
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3337642.html
Copyright © 2011-2022 走看看