首页 > 代码库 > jQuery 1.9 Ajax代码带注释

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 settingsjQuery.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 =http://www.mamicode.com/== "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 xhrsfunction 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 propertiesxhrSupported = jQuery.ajaxSettings.xhr();jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );xhrSupported = jQuery.support.ajax = !!xhrSupported;// Create transport if the browser can provide an xhrif ( 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 1.9 Ajax代码带注释