zoukankan      html  css  js  c++  java
  • jQuery 1.9 Ajax代码带注释

    /* -----------ajax模块开始 -----------*/
    var
        // Document location
        ajaxLocParts,
        ajaxLocation,
        ajax_nonce = jQuery.now(),
    
        ajax_rquery = /?/,
        rhash = /#.*$/,
        rts = /([?&])_=[^&]*/,
        rheaders = /^(.*?):[ 	]*([^
    ]*)
    ?$/mg, // IE leaves an 
     character at EOL
        // #7653, #8125, #8152: local protocol detection
        rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
        rnoContent = /^(?:GET|HEAD)$/,
        rprotocol = /^///,
        rurl = /^([w.+-]+:)(?://([^/?#:]*)(?::(d+)|)|)/,  //例如:["http://localhost:8080", "http:", "localhost", "8080"]
    
        // Keep a copy of the old load method
        //在ajax中会给jQuery原型定义load函数。 这里使用_load存储可能的之前就定义了的load函数。
        //jquery中,load函数有两种不同的用途。
        //$elem.load(fun)  load事件的监听器函数
        //$elem.load(url, params, callback )  通过ajax加载文档到当前元素下
        _load = jQuery.fn.load,
    
        /* Prefilters
         * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
         * 2) These are called:
         *    - BEFORE asking for a transport
         *    - AFTER param serialization (s.data is a string if s.processData is true)
         * 3) key is the dataType
         * 4) the catchall symbol "*" can be used
         * 5) execution will start with transport dataType and THEN continue down to "*" if needed
         */
        /*
        存储通过ajaxPrefilter函数添加的前置过滤函数;
        用途: 针对ajax设置的datatype,添加过滤函数; 例如对请求script的ajax,在序列化参数data之后,发送请求之前,对请求进行修改过滤操作。
        例:prefiters = {"script":[function(){},function(){}],"text":[function(){}],"*":[function(){}]}
        根据每次ajax请求的数据类型调用不同的函数队列,之后"*"对应的的队列也会被调用
        */
    
        prefilters  = {},
    
        /* Transports bindings
         * 1) key is the dataType
         * 2) the catchall symbol "*" can be used
         * 3) selection will start with transport dataType and THEN go to "*" if needed
         */
        // transports存储通过ajaxTransport函数添加的传输函数;  传输函数就是发送请求,返回结果这一过程的代理。
        //意味着你可以很灵活的针对某一ajax请求的数据类型使用自己方式来得到和处理数据
        //其结构同prefilters
        //"*"可以用来处理所有数据类型的请求
        transports = {},
    
        // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
        allTypes = "*/".concat("*");  //   "*/*"
    
    // #8138, IE may throw an exception when accessing
    // a field from window.location if document.domain has been set
    //IE中读取location.href可能会出错。
    try {
        ajaxLocation = location.href;
    } catch( e ) {
        // Use the href attribute of an A element
        // since IE will modify it given document.location
        ajaxLocation = document.createElement( "a" );
        ajaxLocation.href = "";
        ajaxLocation = ajaxLocation.href;
    }
    // Segment location into parts
    //把页面地址分解。
    ////例如:["http://localhost:8080", "http:", "localhost", "8080"]
    ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
    
    // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
    //structure参数可以是前面定义的prefilters或者transports用来存储的对象。
    //返回一个函数。 如果structure参数传入的是prefiler对象,那么返回的函数被jQuery.ajaxPrefilter引用。
    //如果参数是transport对象,那么返回的函数被jQuery.ajaxTransport引用。  (见后面的代码)
    // ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
    // ajaxTransport: addToPrefiltersOrTransports( transports ),
    // jQuery.ajaxPrefilter 和 jQuery.ajaxTransport 函数的区别就在于,两者使用的存储对象不同。
    function addToPrefiltersOrTransports( structure ) {
        // dataTypeExpression is optional and defaults to "*"
        //dataTypeExpression参数可选,默认为"*"
        //dataTypeExpression可以是空格分割的多个dataType。例:  "script json"
        return function( dataTypeExpression, func ) {
            //省略dataTypeExpression时
            //参数修正
            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                dataTypeExpression = "*";
            }
    
            var dataType,
                i = 0,
                 // "script json" --> ["script","json"]
                dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; //切割dataTypeExpression成数组
    
            if ( jQuery.isFunction( func ) ) {
                // For each dataType in the dataTypeExpression
                while ( (dataType = dataTypes[i++]) ) {
                    // Prepend if requested
                    //如果datatype以+开头,表示函数应该被插入到相应的调用函数队列头部
                    if ( dataType[0] === "+" ) {  //string.charAt(0)
                        dataType = dataType.slice( 1 ) || "*";
                        //在存储对象中,每种dataType对应一个函数队列。
                        (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
    
                    // Otherwise append
                    //函数被插入到相应的调用函数队列尾部
                    } else {
                        (structure[ dataType ] = structure[ dataType ] || []).push( func );
                    }
                }
            }
        };
    }
    
    // Base inspection function for prefilters and transports
    //options:the request options   与ajaxSetting合并后的options
    //originalOptions: ajax函数传入的options
    //jqXHR: the jqXHR object of the request  
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
    
        var inspected = {},
            seekingTransport = ( structure === transports );
    
        function inspect( dataType ) {
            var selected;
            inspected[ dataType ] = true;
            jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
                //调用绑定的函数
                var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
                //对于prefilter调用,如果上面函数返回的是代表datatype的字符串,并且此种datatype的前置过滤函数队列未调用过,那么跳转到执行此datatype的前置过滤函数队列
                if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
                    options.dataTypes.unshift( dataTypeOrTransport );//新的dataType添加到options.dataTypes头部
                    inspect( dataTypeOrTransport ); //跳转到新的dataType队列
                    return false; //返回false终止jQuery.each操作。终止当前dataType的前置过滤函数队列的调用。
                } else if ( seekingTransport ) { //对于Transport调用,dataTypeOrTransport变量应该是一个表示传输过程的对象。
                    return !( selected = dataTypeOrTransport );   //返回false终止jQuery.each操作。selected变量指向这个对象。
                }
            });
            return selected;  //对于Transport调用,返回得到的传输对象或者undefined。对于prefilter调用,返回undefined
        }
        //如果dataType不是"*" ,调用inspect(dataType)后,继续调用inspect("*")
        //因为inspect函数对于prefilter和transport调用的返回值不一样,所有:
        //对于prefilter,先inspect(options.dataTypes[0]),再inspect(dataTypes["*"])
        //对于transport,先inspect(options.dataTypes[0]),如果得到传输对象则继续。否则检查"*"尝试得到传输对象。
        //    注意:只使用dataTypes[0]  
        //inspected数组用来防止重复调用,例如dataTypes[0] =="*"的情况。
        return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
    }
    
    // A special extend for ajax options
    // that takes "flat" options (not to be deep extended)
    // Fixes #9887
    //jQuery.ajaxSettings.flatOptions中定义的的属性为浅扩展,其它属性为深扩展。
    function ajaxExtend( target, src ) {
        var deep, key,
            flatOptions = jQuery.ajaxSettings.flatOptions || {};//不需要深扩展的属性的集合。
            //如果属性不需要深扩展,直接赋值给target
            //否则添加到deep对象中
        for ( key in src ) {
            if ( src[ key ] !== undefined ) {
                ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
            }
        }
        if ( deep ) {
            jQuery.extend( true, target, deep );//jQuery.extend函数设置第一个参数deep为true,深扩展
        }
    
        return target;
    }
    
    jQuery.fn.load = function( url, params, callback ) {
        //如果第一个参数类型非string,那么此load函数调用的目的是为了绑定javascript load事件处理程序
        // $("#image").load(handler)
        if ( typeof url !== "string" && _load ) {  //_load是对先前定义的load函数的缓存。
            return _load.apply( this, arguments );
        }
        // url   -->  "tt/test.jsp #selector"
        var selector, response, type,
            self = this,
            off = url.indexOf(" ");
    
        if ( off >= 0 ) {
            selector = url.slice( off, url.length );
            url = url.slice( 0, off );
        }
    
        // If it's a function
        //修正参数
        if ( jQuery.isFunction( params ) ) {
    
            // We assume that it's the callback
            callback = params;
            params = undefined;
    
        // Otherwise, build a param string
        //如果params是一个对象,修改type为post
        } else if ( params && typeof params === "object" ) {
            type = "POST";
        }
    
        // If we have elements to modify, make the request
        //确保当前jQuery对象不是空集合,否则ajax请求毫无意义。
        if ( self.length > 0 ) {
            jQuery.ajax({
                url: url,
                // if "type" variable is undefined, then "GET" method will be used
                type: type,
                dataType: "html",
                data: params
            }).done(function( responseText ) {
    
                // Save response for use in complete callback
                response = arguments;
    
                self.html( selector ?
    
                    // If a selector was specified, locate the right elements in a dummy div
                    // Exclude scripts to avoid IE 'Permission Denied' errors
                    jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
    
                    // Otherwise use the full result
                    responseText );
    
            }).complete( callback && function( jqXHR, status ) {
                //在jQuery对象上调用each方法。 
                //each第二个参数是一个数组或者伪数组如arguments,此时数组中的元素就是是遍历过程中每次传递给callback的参数。
                self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
            });
        }
    
        return this;
    };
    
    // Attach a bunch of functions for handling common AJAX events
    //创建用于绑定全局ajax事件处理器的系列函数
    //$(document).ajaxStart(callBack);
    jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
        jQuery.fn[ type ] = function( fn ){
            return this.on( type, fn );  //通过on函数绑定对应的事件处理器
        };
    });
    //get和post快捷函数定义
    jQuery.each( [ "get", "post" ], function( i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {
            // shift arguments if data argument was omitted
            //修正参数,如果省略了data参数
            if ( jQuery.isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = undefined;
            }
    
            return jQuery.ajax({
                url: url,
                type: method,
                dataType: type,
                data: data,
                success: callback
            });
        };
    });
    
    jQuery.extend({
    
        // Counter for holding the number of active queries
        //此时存在的其它未完成的ajax请求数
        active: 0,
    
        // Last-Modified header cache for next request
        lastModified: {},
        etag: {},
        //默认ajax设置
        ajaxSettings: {
            url: ajaxLocation,  //默认url为当前文档地址
            type: "GET", //默认get方式
            isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),  //是否是本地文件系统 如:浏览器地址以file:///开头
            global: true, //是否支持全局ajax事件
            processData: true, //是否把data选项值处理成字符串形式。
            async: true,//是否异步
            contentType: "application/x-www-form-urlencoded; charset=UTF-8", //请求头中默认的contentType
            /*
            timeout: 0,
            data: null,
            dataType: null,
            username: null,
            password: null,
            cache: null,
            throws: false,  //设为true时转换错误时将错误throw
            traditional: false,
            headers: {},
            */
             
            //如果Ajax请求未设置具体的dataType
            //jQuery通过这个对象,根据使用正则来匹配响应头的content-type值,对应于正则的属性名就被认为是返回内容的数据类型。
            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            },
            //响应对象中的字段到jqXHR对象中字段的映射
            responseFields: {
                xml: "responseXML",
                text: "responseText"
            },
    
            // Data converters
            // Keys separate source (or catchall "*") and destination types with a single space
            //数据转换工具 
            //例如 "text json": jQuery.parseJSON  意思就是可以通过jQuery.parseJSON函数,将text类型的数据转换为json类型的数据
            converters: {
    
                // Convert anything to text
                "* text": window.String,
    
                // Text to html (true = no transformation)
                "text html": true,
    
                // Evaluate text as a json expression
                "text json": jQuery.parseJSON,
    
                // Parse text as xml
                "text xml": jQuery.parseXML
            },
    
            // For options that shouldn't be deep extended:
            // you can add your own custom options here if
            // and when you create one that shouldn't be
            // deep extended (see ajaxExtend)
            //用来设置那些不应该被深扩展的属性
            //ajaxExtend中用到
            flatOptions: {
                url: true,
                context: true
            }
        },
    
        // Creates a full fledged settings object into target
        // with both ajaxSettings and settings fields.
        // If target is omitted, writes into ajaxSettings.
        //如果调用时只传入一个参数,那么扩展的目标对象是ajaxSettings。 (jQuery用户这样调用来扩展全局默认Ajax设置,所有的ajax请求都会受此影响)
        //否则使用setting(jQuery用户设置)和ajaxSettings(默认全局设置)一起扩展到target(目标)对象 (jQuery内部调用)
        ajaxSetup: function( target, settings ) {
            return settings ?
    
                // Building a settings object
                ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
    
                // Extending ajaxSettings
                ajaxExtend( jQuery.ajaxSettings, target );
        },
        //定义ajaxPrefilter和ajaxTransport方法
        ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
        ajaxTransport: addToPrefiltersOrTransports( transports ),
    
        // Main method
        // 调用方式 jQuery.ajax(url[,options])或者jQuery.ajax([options])
        /*
        *@param options 用户设置的选项,用来配置ajax
        */
        ajax: function( url, options ) {
    
            // If url is an object, simulate pre-1.5 signature
            //参数修正。
            if ( typeof url === "object" ) {
                options = url;
                url = undefined;
            }
    
            // Force options to be an object
            options = options || {};
    
            var // Cross-domain detection vars
                parts,
                // Loop variable
                i,
                // URL without anti-cache param
                cacheURL,
                // Response headers as string
                responseHeadersString,
                // timeout handle
    
                timeoutTimer,
    
                // To know if global events are to be dispatched
                fireGlobals,
    
                transport,
                // Response headers
                responseHeaders,
                // Create the final options object
                // ajaxSetting 和options都扩展到{}中
                //s是默认设置与用户设置的选项两者扩展后的对象,综合了用户选项和默认设置。
                s = jQuery.ajaxSetup( {}, options ),
                // Callbacks context
                //如果用户选项没有设置context,那么callbackContext的值默认为s对象
                callbackContext = s.context || s,
                // Context for global events is callbackContext if it is a DOM node or jQuery collection
                //如果设置的context是一个DOM元素或者jQuery对象,则设置globalEventContext值为此context构建的jquery对象
                //否则否则globalEventContext值为jQuery.event对象
                globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
                    jQuery( callbackContext ) :
                    jQuery.event,
                // Deferreds
                deferred = jQuery.Deferred(), 
                //jQuery.Callbacks方法构造一个"once memory"的回调队列
                completeDeferred = jQuery.Callbacks("once memory"), 
                // Status-dependent callbacks
                //用户设置的status选项
                //key为status ,value为函数集合
                //根据状态码设置回调函数 (不同的状态码对应不同的函数列表)
                statusCode = s.statusCode || {},
                // Headers (they are sent all at once)
                requestHeaders = {},
                requestHeadersNames = {},
                // The jqXHR state
                state = 0,
                // Default abort message
                strAbort = "canceled",
                // Fake xhr
                //JjQuery封装的jqXHR对象
                jqXHR = {
                    readyState: 0,
    
                    // Builds headers hashtable if needed
                    getResponseHeader: function( key ) {
                        var match;
                        if ( state === 2 ) { // state==2代表http请求数据过程完成
                            if ( !responseHeaders ) { // responseHeadersString还未转换到responseHeaders。 转换之。
                                responseHeaders = {};
                                while ( (match = rheaders.exec( responseHeadersString )) ) { //正则有g和m,每行匹配一次
                                    responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
                                }
                            }
                            match = responseHeaders[ key.toLowerCase() ];
                        }
                        //将undefined转换为null
                        //返回null或者字符串。
                        return match == null ? null : match;
                    },
    
                    // Raw string
                    getAllResponseHeaders: function() { 
                        return state === 2 ? responseHeadersString : null;
                    },
    
                    // Caches the header
                    //requestHeadersNames中缓存name的小写形式到的映射name      -->  key is  lname , value is name
                    //requestHeaders中缓存name到value的映射  --> key is name, value is value
                    setRequestHeader: function( name, value ) {
                        var lname = name.toLowerCase();
                        if ( !state ) { //state== 0 代表http请求过程还未开始。
                            //将name值在requestHeadersNames中作为属性名为小写形式lname的值缓存
                            //key  is lname,value is name;
                            name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
                            //set
                            requestHeaders[ name ] = value;
                        }
                        return this;
                    },
    
                    // Overrides response content-type header
                    //用来设置s.mimeType
                    overrideMimeType: function( type ) {
                        if ( !state ) {
                            s.mimeType = type;
                        }
                        return this;
                    },
    
                    // Status-dependent callbacks
                    //当前jqXHR状态不同,函数的用途不同。
                    //jqXHR完成状态时,根据其状态码调用回调函数。
                    //否则添加回调函数。
                    statusCode: function( map ) {
                        var code;
                        if ( map ) {
                            if ( state < 2 ) {//给statusCode添加回调函数,前提是Ajax请求此时是未完成状态
                                for ( code in map ) {
                                    // Lazy-add the new callback in a way that preserves old ones
                                    //statusCode[ code ]可能已经设置过。 已经是一个函数或一个函数数组
                                    //新加入的函数或者函数数组放在数组的新建数组的尾部 ,最后触发的时候相当于在时间上后于以前加入的函数处理
                                    // 最后调用的时候,这个多层数组最后会使用Callbacks.add函数添加,这个函数对此进行了处理。
                                    statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
                                }
                            } else { //请求已经响应,直接执行对应的回调
                                // Execute the appropriate callbacks
                                jqXHR.always( map[ jqXHR.status ] );
                            }
                        }
                        return this;
                    },
    
                    // Cancel the request
                    //用来取消ajax请求
                    abort: function( statusText ) {
                        var finalText = statusText || strAbort;
                        if ( transport ) { //调用传输对象的abort方法,终止传输
                            transport.abort( finalText );
                        }
                        done( 0, finalText );
                        return this;
                    }
                };
    
            // Attach deferreds
            //通过deferred对象得到的promise对象
            //jqXHR对象继承promise对象,并添加complete方法,该方法引用了completeDeferred.add方法
            deferred.promise( jqXHR ).complete = completeDeferred.add;
            jqXHR.success = jqXHR.done;
            jqXHR.error = jqXHR.fail;
    
            // Remove hash character (#7531: and string promotion)
            // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
            // Handle falsy url in the settings object (#10093: consistency with old signature)
            // We also use the url parameter if available
            //移除url中的hash字符串
            //如果url以//开头,则添加协议名。(IE7的问题);
            s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
    
            // Alias method option to type as per ticket #12004
            //method 作为 type的别名
            s.type = options.method || options.type || s.method || s.type;
    
            // Extract dataTypes list
            //dataTypes可以是以空格分隔的字符串,包含多个dataType。默认为“*”  例如: "text xml" 表示将text的响应当成xml对待
            // 转换成数组   "text xml"  -->  ["text","xml"]
            s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
    
            // A cross-domain request is in order when we have a protocol:host:port mismatch
            // 非跨域请求需要满足 协议名 主机名 端口都匹配
            if ( s.crossDomain == null ) {
                parts = rurl.exec( s.url.toLowerCase() );
                //比较协议名,域名,端口号,三者任一不同,则为跨域。
                s.crossDomain = !!( parts &&
                    ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || 
                        ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=  //如果无端口号,那么对于http协议默认是80,否则为443(主要用于https)
                            ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
                );
            }
    
            // Convert data if not already a string
            //s.processData 默认为true ,即data选项(请求数据)默认被转换成字符串形式。 值为false时不转换data属性值。
            //通过jQuery.param函数来转换
            if ( s.data && s.processData && typeof s.data !== "string" ) {
                s.data = jQuery.param( s.data, s.traditional ); //s.traditional 可以设置转换是否使用传统模式
            }
    
            // Apply prefilters
            //应用前置过滤
            inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
            //如果在prefilters的处理函数中调用了jqXHR的abort函数,state会被设置为2;
            // If request was aborted inside a prefilter, stop there
            if ( state === 2 ) {
                return jqXHR;
            }
    
            // We can fire global events as of now if asked to
            //是否触发ajax全局事件标志
            fireGlobals = s.global;
    
            // Watch for a new set of requests
            //global标志为true时,当前不存在其它未完成的ajax请求,触发ajaxStart事件。 
            // jQuery.active记录未完成的ajax请求数量
            if ( fireGlobals && jQuery.active++ === 0 ) {
                //jQuery.event.triggerr函数在调用时如果第三个参数elem为空时,默认是在document上触发。
                jQuery.event.trigger("ajaxStart");
            }
    
            // Uppercase the type
            s.type = s.type.toUpperCase();
    
            // Determine if request has content
            // post请求有请求体,即数据通过请求体发送,而不是通过url
            s.hasContent = !rnoContent.test( s.type );
    
            // Save the URL in case we're toying with the If-Modified-Since
            // and/or If-None-Match header later on
            cacheURL = s.url;
    
            // More options handling for requests with no content
            //get请求
            if ( !s.hasContent ) {
    
                // If data is available, append data to url
                // 将data数据添加到url中
                if ( s.data ) {
                    cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
                    // #9682: remove data so that it's not used in an eventual retry
                    delete s.data; //删除data
                }
    
                // Add anti-cache in url if needed
                //如果设置不要缓存,在url中加入一个_参数,其值为随机数。以此来破坏浏览器缓存机制
                if ( s.cache === false ) {
                    s.url = rts.test( cacheURL ) ?
    
                        // If there is already a '_' parameter, set its value
                        //如果参数中已经存在一个参数名"_",覆盖它的值。
                        cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
    
                        // Otherwise add one to the end
                        cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
                }
            }
    
            // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
            //设置s.ifModified为true后,如果服务器的内容未改变,那么服务器会返回不带数据的304报文。数据直接在缓存中得到
            //lastModified和etag同时使用
            if ( s.ifModified ) {
                if ( jQuery.lastModified[ cacheURL ] ) {  //在jquery的lastModified缓存中查找当前url.
                    jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
                }
                if ( jQuery.etag[ cacheURL ] ) {
                    jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
                }
            }
    
            // Set the correct header, if data is being sent
            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
                jqXHR.setRequestHeader( "Content-Type", s.contentType );
            }
    
            // Set the Accepts header for the server, depending on the dataType
            //学习学习 
            // Accept-Language: fr; q=1.0, en; q=0.5   法语和英语都可以 最好是法语
            // Accept: text/html; q=1.0, text; q=0.8, image/gif; q=0.6, image/jpeg; q=0.6, image/*; q=0.5, *; q=0.1
            //逗号分隔不同的选项,分号后面的代表优先级。     
            jqXHR.setRequestHeader(
                "Accept",
                s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?  
                    // "*/*"表示任意类型,分号后面的q=0.01表示优先级啦。
                    //多个类型直接用分号隔开
                    s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
                    s.accepts[ "*" ]
            );
    
            // Check for headers option
            //s.headers对象中的元素复制到RequestHeaders中
            for ( i in s.headers ) {
                jqXHR.setRequestHeader( i, s.headers[ i ] );
            }
    
            // Allow custom headers/mimetypes and early abort
            //beforeSend事件绑定的函数如果返回false或者在函数中设置state为2,那么调用jqXHR.abort()方法,终止请求
            //如果在jqXHR中也调用了abort方法,那么肯定会导致abort方法中的transport.abort方法再次执行,这样不会有问题么。。。
            if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
                // Abort if not done already and return
                return jqXHR.abort();
            }
    
            // aborting is no longer a cancellation
            strAbort = "abort";
    
            // Install callbacks on deferreds
            //将options中的success,error ,complete属性方法使用jqXHR对应的监听器注册。
            //    如: jqXHR["success"](callBack);
            for ( i in { success: 1, error: 1, complete: 1 } ) {
                jqXHR[ i ]( s[ i ] );
            }
    
            // Get transport
            //获取传输对象。
            transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
    
            // If no transport, we auto-abort
            if ( !transport ) {
                done( -1, "No Transport" );
            } else {
                jqXHR.readyState = 1;  //找到transport后,jqXHR.readyState变为1,标志jqXHR开始
    
                // Send global event
                //触发全局ajaxSend事件
                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
                }
                // Timeout
                //异步模式下,timeout设置最大等待时间
                if ( s.async && s.timeout > 0 ) {
                    timeoutTimer = setTimeout(function() {
                        jqXHR.abort("timeout");
                    }, s.timeout );
                }
    
                try {
                    state = 1;  //state设置为1,标志传输过程开始
                    //调用transport的send方法,传入请求头和回调函数
                    //注意回调函数时done函数
                    transport.send( requestHeaders, done );
                } catch ( e ) {
                    // Propagate exception as error if not done
                    if ( state < 2 ) {
                        done( -1, e );
                    // Simply rethrow otherwise
                    } else {
                        throw e;
                    }
                }
            }
    
            // Callback for when everything is done
                //transport.send完成后的回调函数,或者出错时手动调用
            //四个参数 
            //status 和 statusText    例如  2 "success"
            //responses   对象   例如   {xml:"someString",html:"someString"}
            //headers 包含所有响应头信息的字符串 
            function done( status, nativeStatusText, responses, headers ) {
                var isSuccess, success, error, response, modified,
                    statusText = nativeStatusText;
    
                // Called once
                //已经调用过done了
                if ( state === 2 ) {
                    return;
                }
    
                // State is "done" now
                //state = 2意味着传输过程完成
                state = 2;
    
                // Clear timeout if it exists
                //清除定时任务
                if ( timeoutTimer ) {
                    clearTimeout( timeoutTimer );
                }
    
                // Dereference transport for early garbage collection
                // (no matter how long the jqXHR object will be used)
                //清除transport传输对象
                transport = undefined;
    
                // Cache response headers
                //headers复制给responseHeadersString
                responseHeadersString = headers || "";
    
                // Set readyState
                //设置readyState
                //status>0时 jqXHR.readyState设置为4,标志jqXHR过程成功完成。
                //jqXHR.readyState设置为0时,表示jqXHR过程失败
                jqXHR.readyState = status > 0 ? 4 : 0;
    
                // Get response data
                //调用ajaxHandleResponses处理response
                if ( responses ) {
                    response = ajaxHandleResponses( s, jqXHR, responses );
                }
    
                // If successful, handle type chaining
                //success
                if ( status >= 200 && status < 300 || status === 304 ) {
    
                    // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
                    if ( s.ifModified ) {
                        modified = jqXHR.getResponseHeader("Last-Modified");
                        if ( modified ) {
                            //缓存当前url的最近修改时间
                            jQuery.lastModified[ cacheURL ] = modified;
                        }
                        modified = jqXHR.getResponseHeader("etag");
                        if ( modified ) {
                            //缓存etag值
                            jQuery.etag[ cacheURL ] = modified;
                        }
                    }
    
                    // if no content
                    // 204   no content
                    if ( status === 204 ) {
                        isSuccess = true;
                        statusText = "nocontent";
    
                    // if not modified
                    // 304 notmodified
                    } else if ( status === 304 ) {  //返回304的话,怎么从缓存中拿到数据?
                        isSuccess = true;
                        statusText = "notmodified";
    
                    // If we have data, let's convert it
                    // ajaxConvert
                    //否则就是得到数据的情况了。
                    } else {
                        isSuccess = ajaxConvert( s, response );
                        statusText = isSuccess.state;
                        success = isSuccess.data;
                        error = isSuccess.error;
                        isSuccess = !error;
                    }
                } else { //failed
                    // We extract error from statusText
                    // then normalize statusText and status for non-aborts
                    error = statusText;
                    if ( status || !statusText ) {
                        statusText = "error";
                        if ( status < 0 ) {
                            status = 0;
                        }
                    }
                }
    
                // Set data for the fake xhr object
                jqXHR.status = status;
                //优先使用参数传入的nativeStatusText
                jqXHR.statusText = ( nativeStatusText || statusText ) + "";
    
                // Success/Error
                //触发success 或者 error
                if ( isSuccess ) {
                    deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
                } else {
                    deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
                }
    
                // Status-dependent callbacks
                //根据当前响应的状态码触发options选项statusCode属性对象中对应的函数
                jqXHR.statusCode( statusCode );
                statusCode = undefined;
                //如果没给定context,那么调用jQuery.event.trigger函数触发这两个事件,此时默认context为document
                //否则在context上触发  ajaxSuccess 或者 ajaxError
                if ( fireGlobals ) {
                    globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
                        [ jqXHR, s, isSuccess ? success : error ] );
                }
    
                // Complete
                //触发当前ajax通过complete选项绑定的回调函数
                completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
                //触发全局绑定的ajaxComplete 
                // 所有的ajax请求都执行完了就触发"ajaxStop"
                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
                    // Handle the global AJAX counter
                    //--jQuery.active
                    if ( !( --jQuery.active ) ) {  //所有的ajax请求都执行完了就触发ajaxStop
                        jQuery.event.trigger("ajaxStop");
                    }
                }
            }
            //返回jqXHR对象
            return jqXHR;
        },
    
        getScript: function( url, callback ) {
            return jQuery.get( url, undefined, callback, "script" );
        },
    
        getJSON: function( url, data, callback ) {
            return jQuery.get( url, data, callback, "json" );
        }
    });
    
    /* Handles responses to an ajax request:
     * - sets all responseXXX fields accordingly
     * - finds the right dataType (mediates between content-type and expected dataType)
     * - returns the corresponding response
     */
     /*设置jqXHR的responseXXX属性
     找到正确的dataType(介于responses中dataType与期待的dataType可以直接转换的类型),并且添加到dataTypes中
     返回响应内容*/
     //这里需要距离。  因为responses中可能含有多种类型dataType的值,比如xml或者html,对着两种类型都尝试是否能直接转换成期待的dataType
     //responses  可能-->   {xml:"somestring",html:"someString"}
     //把responses中的类型添加到dataTypes中,优先添加能直接转换的,否则添加第一个属性。如果添加的type和dataTypes[0]重合则不需要添加。
      //返回responses中对应添加类型的值。
    function ajaxHandleResponses( s, jqXHR, responses ) {
        var firstDataType, ct, finalDataType, type,
            contents = s.contents,  //contents选项
    /*contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            }*/
            dataTypes = s.dataTypes,
            responseFields = s.responseFields;
            /*responseFields: {
                xml: "responseXML",
                text: "responseText"
            }*/
            //responseFields 包含response中需要转换到jqXHR中的字段.  key为response中需要转换的属性名  value为转换到jqXHR中的属性名  
        for ( type in responseFields ) {
            if ( type in responses ) {
                jqXHR[ responseFields[type] ] = responses[ type ];  //例如:  jqXHR["responseXML"] = responses["xml"]
            }
        }
    
        // Remove auto dataType and get content-type in the process
        //将dataTypes数组前面的所有"*"移除
        //dataTypes前面必须是一个"*",ct才会被赋值
        while( dataTypes[ 0 ] === "*" ) {
            dataTypes.shift();
            if ( ct === undefined ) {//初始化ct为content-type
                ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); //s.mimeType选项值覆盖响应的content-type
            }
        }
    
        // Check if we're dealing with a known content-type
        //对于上面: ct未赋值的情况,说明dataTypes[0]!="*" 那么默认dataTypes[0] 就是响应头content-type在contents中对应的属性名
        //否则以响应的content-type对应的type作为datatypes的第一个元素
        if ( ct ) {
            for ( type in contents ) {//遍历contents
                if ( contents[ type ] && contents[ type ].test( ct ) ) { //执行type对应的正则来匹配响应头中的content-type
                    dataTypes.unshift( type ); //匹配到的type添加到dataTypes数组前面
                    break;
                }
            }
        }
    
        // Check to see if we have a response for the expected dataType
        if ( dataTypes[ 0 ] in responses ) {
            finalDataType = dataTypes[ 0 ];
        } else {
            // Try convertible dataTypes
            //否则,因为response可能包含多个属性,对每个属性都尝试是否可以直接转换(通过检查s.converters)
            for ( type in responses ) {
                //直接可以转换或者dataTypes为空时
                if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
                    finalDataType = type; 
                    break;
                }
                if ( !firstDataType ) { //responses中第一个不能转换的type
                    firstDataType = type;
                }
            }
            // Or just use first one
            //找不到可以直接转换的类型,那么finalDataType就是responses中第一个不能转换的type
            finalDataType = finalDataType || firstDataType;
        }
    
        // If we found a dataType
        // We add the dataType to the list if needed
        // and return the corresponding response
        if ( finalDataType ) {
    
            if ( finalDataType !== dataTypes[ 0 ] ) { //如果是中间type。
                dataTypes.unshift( finalDataType );//添加到dataTypes前面
            }
            return responses[ finalDataType ];//返回response中对应finalDataType类型的数据
        }
    }
    
    // Chain conversions given the request and the original response
    //链式转换。
    //将response转换按照dataTypes中的类型依次转换,最后返回一个封装后的结果。  成功时:{ state: "success", data: response };
    function ajaxConvert( s, response ) {
        var conv2, current, conv, tmp,
            converters = {},
            i = 0,
            // Work with a copy of dataTypes in case we need to modify it for conversion
            //复制s.dataTypes,通过调用s.dataTypes.slice();
            dataTypes = s.dataTypes.slice(),
            prev = dataTypes[ 0 ];
    
        // Apply the dataFilter if provided
        //dataFilter方法先于类型转换。
        if ( s.dataFilter ) {
            response = s.dataFilter( response, s.dataType );
        }
    
        // Create converters map with lowercased keys
        if ( dataTypes[ 1 ] ) {
            for ( conv in s.converters ) {
                converters[ conv.toLowerCase() ] = s.converters[ conv ];
            }
        }
    
        // Convert to each sequential dataType, tolerating list modification
        //循环dataTypes中相邻的两个元素,判断其是否可以直接转换。
        //如果不能直接转换,那么尝试曲线救国 即A-->C行不通    找看看A-->B-->C
        for ( ; (current = dataTypes[++i]); ) {
    
            // There's only work to do if current dataType is non-auto
            if ( current !== "*" ) {
    
                // Convert response if prev dataType is non-auto and differs from current
                if ( prev !== "*" && prev !== current ) {
    
                    // Seek a direct converter
                    conv = converters[ prev + " " + current ] || converters[ "* " + current ];
    
                    // If none found, seek a pair
                    //如果不能直接转换
                    if ( !conv ) {
                        //遍历converters
                        for ( conv2 in converters ) {
    
                            // If conv2 outputs current
                            tmp = conv2.split(" ");
                            if ( tmp[ 1 ] === current ) {
    
                                // If prev can be converted to accepted input
                                conv = converters[ prev + " " + tmp[ 0 ] ] ||
                                    converters[ "* " + tmp[ 0 ] ];
                                if ( conv ) {
                                    // Condense equivalence converters
                                    if ( conv === true ) {
                                        conv = converters[ conv2 ];
    
                                    // Otherwise, insert the intermediate dataType
                                    //这里不需要判断converters[ conv2 ] === true的情况。
                                    //因为在这种情况下conv的值已经是转换函数了。
                                    //如果converters[ conv2 ] !== true,将找到的可以用来作过渡转换的type添加到dataTypes中合适的位置
                                    } else if ( converters[ conv2 ] !== true ) {
                                        current = tmp[ 0 ];
    
                                        dataTypes.splice( i--, 0, current );
                                    }
    
                                    break;  //找到了就中断循环
                                }
                            }
                        }
                    }
    
                    // Apply converter (if not an equivalence)
                    if ( conv !== true ) {
    
                        // Unless errors are allowed to bubble, catch and return them
                        if ( conv && s["throws"] ) {
                            response = conv( response );
                        } else {
                            try {
                                response = conv( response );
                            } catch ( e ) {
                                return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
                            }
                        }
                    }
                }
    
                // Update prev for next iteration
                prev = current;
            }
        }
        //转换完成
        return { state: "success", data: response };
    }
    // Install script dataType
    //扩展jQuery.ajaxSetting对象
    jQuery.ajaxSetup({
        accepts: {
            script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
        },
        contents: {
            script: /(?:java|ecma)script/
        },
        converters: {
            "text script": function( text ) {
                jQuery.globalEval( text );
                return text;
            }
        }
    });
    
    // Handle cache's special case and global
    //script类型请求的前置处理
    //a.默认不使用浏览器缓存
    //b.对于跨域请求:使用get方法,并且设置global为false,即不触发全局ajax对象。
    jQuery.ajaxPrefilter( "script", function( s ) {
        if ( s.cache === undefined ) {
            s.cache = false;
        }
        if ( s.crossDomain ) {
            s.type = "GET";
            s.global = false;
        }
    });
    
    // Bind script tag hack transport
    //请求script文件使用的传输对象。
    jQuery.ajaxTransport( "script", function(s) {
    
        // This transport only deals with cross domain requests
        //只处理跨域的部分
        //可以看到跨域的script文件请求通过新建script标签完成。
        if ( s.crossDomain ) {
    
            var script,
                head = document.head || jQuery("head")[0] || document.documentElement;
    
            return {
    
                send: function( _, callback ) {
    
                    script = document.createElement("script");
    
                    script.async = true;
    
                    if ( s.scriptCharset ) {
                        script.charset = s.scriptCharset;
                    }
    
                    script.src = s.url;
    
                    // Attach handlers for all browsers
                    //isAbort参数在下面定义的abort方法中手动调用script.onload函数时设为true
                    //IE的 script 元素支持onreadystatechange事件,不支持onload事件。
                    //FF的script 元素不支持onreadystatechange事件,只支持onload事件。
                    script.onload = script.onreadystatechange = function( _, isAbort ) {
                        //isAbort时,做清除script的处理
                        //!script.readyState 说明是在FF下面,此时表明load完成
                        ///loaded|complete/.test( script.readyState )表明在IE下需要检测到readyState为loaded或者complete时,才算load完成
                        if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
    
                            // Handle memory leak in IE
                            script.onload = script.onreadystatechange = null;
    
                            // Remove the script
                            if ( script.parentNode ) {
                                script.parentNode.removeChild( script );
                            }
    
                            // Dereference the script
                            script = null;
    
                            // Callback if not abort
                            if ( !isAbort ) {
                                callback( 200, "success" );
                            }
                        }
                    };
    
                    // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
                    // Use native DOM manipulation to avoid our domManip AJAX trickery
                    head.insertBefore( script, head.firstChild );
                },
    
                abort: function() {
                    if ( script ) {
                        script.onload( undefined, true );
                    }
                }
            };
        }
    });
    var oldCallbacks = [], //回调函数名的回收站
        rjsonp = /(=)?(?=&|$)|??/;    // ?= 是正向先行断言
    
    // Default jsonp settings
    jQuery.ajaxSetup({
        jsonp: "callback",
        jsonpCallback: function() {
            //回收站里没得时就新建一个随机函数名
            var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
            this[ callback ] = true; //this指向s
            return callback;
        }
    });
    
    // Detect, normalize options and install callbacks for jsonp requests
    //对json和jsonp类型ajax请求的前置处理
    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
    
        var callbackName, overwritten, responseContainer,
            /*
            先在s.url中寻找jsonp标志'anyCallbackName=?',如果未找到那么尝试在s.data中找标志"anyCallbackName=?" 。  "anyCallbackName"是用于设置回调的参数名,和服务器的设置相关。
            对于get请求,s.data字符串已经被添加到了s.url中,所以如果在s.url中未找到而在s.data中找到了那么一定是post请求。
            jsonProp  -->   false||"url"||"data"
            */
            jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
                "url" :
                typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
            );
        /*对于jsonp请求,可以通过在s.url或者s.data字符串中添加
            "anyCallbackName=?" 或者设置s.jsonp来告诉jQuery这是一jsonp请求。
            s.jsonp选项设置的是服务器相关的jsonp参数名。
            s.jsonpCallback参数可以是函数名字符串或者一个返回函数名字符串的函数。
                推荐是不手动设置此参数,通过jQuery随机生成(注意:手动设置函数名后,如果用户定义了同名函数,jQuery最终也会调用这个函数)。
        */
        // Handle iff the expected data type is "jsonp" or we have a parameter to set
        if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
    
            // Get callback name, remembering preexisting value associated with it
            //s.jsonpCallback 如果是一个函数就取得函数返回值作为回调函数名,否则直接作为回调函数名
            callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
                s.jsonpCallback() :
                s.jsonpCallback;
    
            // Insert callback into url or form data
            //插入callback到url或者data中
            if ( jsonProp ) {//s.url或s.data中插入了"anyCallbackName=?"标志
                s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
            } else if ( s.jsonp !== false ) {//其它情况,即s.url和s.data中都没有手动插入"fun=?"标志,那么自动生成。
                s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
            }
    
            // Use data converter to retrieve json after script execution
            //设置script类型到json类型的转换
            //  因为当前函数最后会  return "script";
            s.converters["script json"] = function() {
                if ( !responseContainer ) {
                    jQuery.error( callbackName + " was not called" );
                }
                return responseContainer[ 0 ];
            };
    
            // force json dataType
            //强制dataType[0] 为"json" .  意味着"jsonp"  也被设置为"json"
            s.dataTypes[ 0 ] = "json";
    
            // Install callback
            overwritten = window[ callbackName ];
            window[ callbackName ] = function() {
                responseContainer = arguments;
            };
    
            // Clean-up function (fires after converters)
            jqXHR.always(function() {
                // Restore preexisting value
                window[ callbackName ] = overwritten;
    
                // Save back as free
                if ( s[ callbackName ] ) {
                    // make sure that re-using the options doesn't screw things around
                    s.jsonpCallback = originalSettings.jsonpCallback;
    
                    // save the callback name for future use
                    oldCallbacks.push( callbackName );
                }
    
                // Call if it was a function and we have a response
                //用户定义的同名函数也会调用。
                if ( responseContainer && jQuery.isFunction( overwritten ) ) {
                    overwritten( responseContainer[ 0 ] );
                }
    
                responseContainer = overwritten = undefined;
            });
    
            // Delegate to script
            //委派到script类型
            return "script";
        }
    });
    var xhrCallbacks, xhrSupported,
        xhrId = 0,
        // #5280: Internet Explorer will keep connections alive if we don't abort on unload
        xhrOnUnloadAbort = window.ActiveXObject && function() {
            // Abort all pending requests
            var key;
            for ( key in xhrCallbacks ) {
                xhrCallbacks[ key ]( undefined, true );
            }
        };
    
    // Functions to create xhrs
    function createStandardXHR() {
        try {
            return new window.XMLHttpRequest();
        } catch( e ) {}
    }
    
    function createActiveXHR() {
        try {
            return new window.ActiveXObject("Microsoft.XMLHTTP");
        } catch( e ) {}
    }
    
    // Create the request object
    // (This is still attached to ajaxSettings for backward compatibility 向后兼容)
    jQuery.ajaxSettings.xhr = window.ActiveXObject ?
        /* Microsoft failed to properly
         * implement the XMLHttpRequest in IE7 (can't request local files),
         * so we use the ActiveXObject when it is available
         * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
         * we need a fallback.
         */
         //在IE下面,ajax不能请求本地文件。
        function() {
            return !this.isLocal && createStandardXHR() || createActiveXHR();
        } :
        // For all other browsers, use the standard XMLHttpRequest object
        createStandardXHR;
    
    // Determine support properties
    xhrSupported = jQuery.ajaxSettings.xhr();
    jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
    xhrSupported = jQuery.support.ajax = !!xhrSupported;
    
    // Create transport if the browser can provide an xhr
    if ( xhrSupported ) {
    
        jQuery.ajaxTransport(function( s ) {  //创建"*"对应的transport,即默认处理所有请求的transport
            // Cross domain only allowed if supported through XMLHttpRequest
            //跨域请求需要支持withCredentials属性的浏览器
            if ( !s.crossDomain || jQuery.support.cors ) {
    
                var callback;
    
                return {
                    send: function( headers, complete ) {
    
                        // Get a new xhr
                        var handle, i,
                            xhr = s.xhr();
    
                        // Open the socket
                        // Passing null username, generates a login popup on Opera (#2865)
                        if ( s.username ) {
                            xhr.open( s.type, s.url, s.async, s.username, s.password );
                        } else {
                            xhr.open( s.type, s.url, s.async );
                        }
    
                        // Apply custom fields if provided
                        /*
                        例如:xhrFields: {
                          withCredentials: true
                           }
                           用来设置xhr请求的属性。
                       */
                        if ( s.xhrFields ) {
                            for ( i in s.xhrFields ) {
                                xhr[ i ] = s.xhrFields[ i ];
                            }
                        }
    
                        // Override mime type if needed
                        if ( s.mimeType && xhr.overrideMimeType ) {
                            xhr.overrideMimeType( s.mimeType );
                        }
    
                        // X-Requested-With header
                        // For cross-domain requests, seeing as conditions for a preflight are
                        // akin to a jigsaw puzzle, we simply never set it to be sure.
                        // (it can always be set on a per-request basis or even using ajaxSetup)
                        // For same-domain requests, won't change header if already provided.
                        if ( !s.crossDomain && !headers["X-Requested-With"] ) {
                            headers["X-Requested-With"] = "XMLHttpRequest";
                        }
    
                        // Need an extra try/catch for cross domain requests in Firefox 3
                        try {
                            for ( i in headers ) {
                                xhr.setRequestHeader( i, headers[ i ] );
                            }
                        } catch( err ) {}
    
                        // Do send the request
                        // This may raise an exception which is actually
                        // handled in jQuery.ajax (so no try/catch here)
                        xhr.send( ( s.hasContent && s.data ) || null );
    
                        // Listener
                        callback = function( _, isAbort ) {
                            var status, responseHeaders, statusText, responses;
    
                            // Firefox throws exceptions when accessing properties
                            // of an xhr when a network error occurred
                            // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
                            try {
    
                                // Was never called and is aborted or complete
                                if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
    
                                    // Only called once
                                    callback = undefined;
    
                                    // Do not keep as active anymore
                                    if ( handle ) {
                                        xhr.onreadystatechange = jQuery.noop;
                                        if ( xhrOnUnloadAbort ) {
                                            delete xhrCallbacks[ handle ];
                                        }
                                    }
    
                                    // If it's an abort
                                    if ( isAbort ) {
                                        // Abort it manually if needed
                                        if ( xhr.readyState !== 4 ) {
                                            xhr.abort();
                                        }
                                    } else {
                                        responses = {};
                                        status = xhr.status;
                                        responseHeaders = xhr.getAllResponseHeaders();
    
                                        // When requesting binary data, IE6-9 will throw an exception
                                        // on any attempt to access responseText (#11426)
                                        if ( typeof xhr.responseText === "string" ) {
                                            responses.text = xhr.responseText;
                                        }
    
                                        // Firefox throws an exception when accessing
                                        // statusText for faulty cross-domain requests
                                        try {
                                            statusText = xhr.statusText;
                                        } catch( e ) {
                                            // We normalize with Webkit giving an empty statusText
                                            statusText = "";
                                        }
    
                                        // Filter status for non standard behaviors
    
                                        // If the request is local and we have data: assume a success
                                        // (success with no data won't get notified, that's the best we
                                        // can do given current implementations)
                                        if ( !status && s.isLocal && !s.crossDomain ) {
                                            status = responses.text ? 200 : 404;
                                        // IE - #1450: sometimes returns 1223 when it should be 204
                                        } else if ( status === 1223 ) {
                                            status = 204;
                                        }
                                    }
                                }
                            } catch( firefoxAccessException ) {
                                if ( !isAbort ) {
                                    complete( -1, firefoxAccessException );
                                }
                            }
    
                            // Call complete if needed
                            if ( responses ) {
                                complete( status, statusText, responses, responseHeaders );
                            }
                        };
    
                        if ( !s.async ) {
                            // if we're in sync mode we fire the callback
                            callback();
                        } else if ( xhr.readyState === 4 ) {
                            // (IE6 & IE7) if it's in cache and has been
                            // retrieved directly we need to fire the callback
                            setTimeout( callback );
                        } else {
                            handle = ++xhrId;
                            if ( xhrOnUnloadAbort ) {
                                // Create the active xhrs callbacks list if needed
                                // and attach the unload handler
                                if ( !xhrCallbacks ) {
                                    xhrCallbacks = {};
                                    jQuery( window ).unload( xhrOnUnloadAbort );
                                }
                                // Add to list of active xhrs callbacks
                                xhrCallbacks[ handle ] = callback;
                            }
                            xhr.onreadystatechange = callback;
                        }
                    },
    
                    abort: function() {
                        if ( callback ) {
                            callback( undefined, true );
                        }
                    }
                };
            }
        });
    }
    //ajax模块结束
  • 相关阅读:
    CFree 提示main must return int
    策略模式
    CFree 提示no newline at the end of file
    EEPROM的写入操作解析
    一些关于mic的知识
    一些关于电池的资料
    太阳能电池板日发电量简易计算方法
    ubuntu 下载编译android源代码
    SC44B0的内存地址解析
    RequireJS 2.0 学习笔记一
  • 原文地址:https://www.cnblogs.com/cheerfulCoder/p/4047150.html
Copyright © 2011-2022 走看看