jQuery 1.9 Ajax代码带注释

/* -----------ajax模块开始 -----------*/

var

    // Document location

    ajaxLocParts,

    ajaxLocation,

    ajax_nonce = jQuery.now(),



    ajax_rquery = /\?/,

    rhash = /#.*$/,

    rts = /([?&])_=[^&]*/,

    rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r 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模块结束

你可能感兴趣的:(jquery)