jQ作为javascript的库( ▼-▼ ), 尽善尽美, 代码优美,  值得学习。  这一周平常上班没啥事也看jQ1.5的代码, 今天周六差不多看完了(Sizzle部分还没看), 重新看了一下, 又有很多新东西;

 

  相对与1.4版本的ajax部分, 整个进行了重写, 实在是坑爹,  现在还有很多没弄懂,  ajax可以非常简单地:

var xhr = new XMLHttpReques || new window.ActiveXObject("Microsoft.XMLHTTP");

  也可以同样可以写几千行,  所有的$.get(),  $.post(), $.getJSON(),  $.getScript().... 都是$.ajax的shortcut, 所有最后都是通过$.ajax 这个方法统一处理, 而且多了传送器这东西, 一下子觉得好酷炫, 而且查资料查到司徒大神2011就开始了解这些东西, 自己实在差太多了, 大家都要加油才行;

    //匹配URL的那种空格;
    var r20 = /%20/g,
        rbracket = /\[\]$/,
    //回车加换行,或者单单回车(for mac);
        rCRLF = /\r?\n/g,
    //是否有#
        rhash = /#.*$/,
        rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
        rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
        rnoContent = /^(?:GET|HEAD)$/,
        rprotocol = /^\/\//,
        rquery = /\?/,
    // "<div>11</div><script>console.log(1)</script><div>11</div>".match(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi);
    // <script>console.log(1)</script>会被匹配出来;
        rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
        rselectTextarea = /^(?:select|textarea)/i,
        rspacesAjax = /\s+/,
        rts = /([?&])_=[^&]*/,
        rurl = /^(\w+:)\/\/([^\/?#:]+)(?::(\d+))?/,

    // Keep a copy of the old load method
        _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
     */
        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 = {};

// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
    // prefilters or transports;
    // "json jsonp" "script" "script" XML请求包装函数;
    function addToPrefiltersOrTransports( structure ) {
        // 又是一个闭包;
        // dataTypeExpression is optional and defaults to "*"
        return function( dataTypeExpression, func ) {
            //debugger;
            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                dataTypeExpression = "*";
            }

            if ( jQuery.isFunction( func ) ) {
                // rspacesAjax = /\s+/;
                var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
                    i = 0,
                    length = dataTypes.length,
                    dataType,
                    list,
                    placeBefore;

                // For each dataType in the dataTypeExpression
                // json jsonp script 或者是 *;
                for(; i < length; i++ ) {
                    dataType = dataTypes[ i ];
                    // We control if we're asked to add before
                    // any existing element
                    // 可能dataTypes是这样的 +json jsonp; 那么这个placeBefore就是ture, 这个回调会被放到了list最前排;
                    placeBefore = /^\+/.test( dataType );
                    if ( placeBefore ) {
                        dataType = dataType.substr( 1 ) || "*";
                    }
                    list = structure[ dataType ] = structure[ dataType ] || [];
                    // then we add to the structure accordingly
                    // 保存回调方法;
                    list[ placeBefore ? "unshift" : "push" ]( func );
                }
            }
        };
    }

//Base inspection function for prefilters and transports
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jXHR,
                                            dataType /* internal */, inspected /* internal */ ) {

        dataType = dataType || options.dataTypes[ 0 ];
        inspected = inspected || {};

        inspected[ dataType ] = true;

        var list = structure[ dataType ],
            i = 0,
            length = list ? list.length : 0,
            executeOnly = ( structure === prefilters ),
            selection;

        for(; i < length && ( executeOnly || !selection ); i++ ) {
            selection = list[ i ]( options, originalOptions, jXHR );
            // If we got redirected to another dataType
            // we try there if not done already
            if ( typeof selection === "string" ) {
                if ( inspected[ selection ] ) {
                    selection = undefined;
                } else {
                    options.dataTypes.unshift( selection );
                    selection = inspectPrefiltersOrTransports(
                        structure, options, originalOptions, jXHR, selection, inspected );
                }
            }
        }
        // If we're only executing or nothing was selected
        // we try the catchall dataType if not done already
        if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
            selection = inspectPrefiltersOrTransports(
                structure, options, originalOptions, jXHR, "*", inspected );
        }
        // unnecessary when only executing (prefilters)
        // but it'll be ignored by the caller in that case
        return selection;
    }

    jQuery.fn.extend({
        load: function( url, params, callback ) {
            if ( typeof url !== "string" && _load ) {
                return _load.apply( this, arguments );

                // Don't do a request if no elements are being requested
            } else if ( !this.length ) {
                return this;
            }

            var off = url.indexOf( " " );
            if ( off >= 0 ) {
                var selector = url.slice( off, url.length );
                url = url.slice( 0, off );
            }

            // Default to a GET request
            var type = "GET";

            // If the second parameter was provided
            if ( params ) {
                // If it's a function
                if ( jQuery.isFunction( params ) ) {
                    // We assume that it's the callback
                    callback = params;
                    params = null;

                    // Otherwise, build a param string
                } else if ( typeof params === "object" ) {
                    params = jQuery.param( params, jQuery.ajaxSettings.traditional );
                    type = "POST";
                }
            }

            var self = this;

            // Request the remote document
            jQuery.ajax({
                url: url,
                type: type,
                dataType: "html",
                data: params,
                // Complete callback (responseText is used internally)
                complete: function( jXHR, status, responseText ) {
                    // Store the response as specified by the jXHR object
                    responseText = jXHR.responseText;
                    // If successful, inject the HTML into all the matched elements
                    if ( jXHR.isResolved() ) {
                        // #4825: Get the actual response in case
                        // a dataFilter is present in ajaxSettings
                        jXHR.done(function( r ) {
                            responseText = r;
                        });
                        // See if a selector was specified
                        self.html( selector ?
                            // Create a dummy div to hold the results
                            jQuery("<div>")
                                // inject the contents of the document in, removing the scripts
                                // to avoid any 'Permission Denied' errors in IE
                                .append(responseText.replace(rscript, ""))

                                // Locate the specified elements
                                .find(selector) :

                            // If not, just inject the full result
                            responseText );
                    }

                    if ( callback ) {
                        self.each( callback, [ responseText, status, jXHR ] );
                    }
                }
            });

            return this;
        },
        /*
         <form >
         <input type="text" name="ipt0" />
         <input type="text"  name="ipt1" />
         </form>

         ==>>  $("#form").serializeArray();
         */
        serialize: function() {
            return jQuery.param( this.serializeArray() );
        },

        serializeArray: function() {
            return this.map(function(){
                return this.elements ? jQuery.makeArray( this.elements ) : this;
            })
                .filter(function(){
                    return this.name && !this.disabled &&
                        ( this.checked || rselectTextarea.test( this.nodeName ) ||
                            rinput.test( this.type ) );
                })
                .map(function( i, elem ){
                    var val = jQuery( this ).val();

                    return val == null ?
                        null :
                        jQuery.isArray( val ) ?
                            jQuery.map( val, function( val, i ){
                                return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                            }) :
                        { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                }).get();
        }
    });

    // Attach a bunch of functions for handling common AJAX events
    // 利用闭包减少代码量;
    jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
        jQuery.fn[ o ] = function( f ){
            return this.bind( o, f );
        };
    } );

    jQuery.each( [ "get", "post" ], function( i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {
            // shift arguments if data argument was omitted
            if ( jQuery.isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = null;
            }

            return jQuery.ajax({
                type: method,
                url: url,
                data: data,
                success: callback,
                dataType: type
            });
        };
    } );

    jQuery.extend({

        getScript: function( url, callback ) {
            return jQuery.get( url, null, callback, "script" );
        },

        getJSON: function( url, data, callback ) {
            return jQuery.get( url, data, callback, "json" );
        },

        ajaxSetup: function( settings ) {
            /*
             setting : {
             jsonp : "callback",
             jsonpCallback : fn
             },
             setting : {
             accepts : {
             script : "text/javascript, application/javascript"
             },
             contents : {
             script : /javascript/ <<==是一个正则;
             },
             converters : function( text ) {
             jQuery.globalEval( text );
             return text;
             }
             }
             */
            //debugger;
            jQuery.extend( true, jQuery.ajaxSettings, settings );
            if ( settings.context ) {
                jQuery.ajaxSettings.context = settings.context;
            }
        },

        ajaxSettings: {
            url: location.href,
            global: true,
            type: "GET",
            contentType: "application/x-www-form-urlencoded",
            processData: true,
            async: true,
            /*
             timeout: 0,
             data: null,
             dataType: null,
             username: null,
             password: null,
             cache: null,
             traditional: false,
             headers: {},
             crossDomain: null,
             */

            accepts: {
                xml: "application/xml, text/xml",
                html: "text/html",
                text: "text/plain",
                json: "application/json, text/javascript",
                "*": "*/*"
            },

            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            },

            responseFields: {
                xml: "responseXML",
                text: "responseText"
            },

            // List of data converters
            // 1) key format is "source_type destination_type" (a single space in-between)
            // 2) the catchall symbol "*" can be used for source_type
            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
            }
        },

        //预传送器, 后面会初始化json和jsonp(包括回调的处理等), 标准xml的预传送器;
        ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
        //标准传送器, 包括ajax的传送器的执行操作, jsonp中需要添加script标签到界面的操作;
        ajaxTransport: addToPrefiltersOrTransports( transports ),

        // file///:C/本地协议的文件;
        // Main method
        /*
         cb = function(arg){console.log(arg)};
         var url = "http://www.filltext.com/?callback=?";
         $.getJSON( url, {
         'callback' : "cb",
         'rows': 5,
         'fname': '{firstName}',
         'lname': '{lastName}',
         'tel': '{phone|format}',
         });
         */
        ajax: function( url, options ) {

            // If options is not an object,
            // we simulate pre-1.5 signature
            if ( typeof options !== "object" ) {
                options = url;
                url = undefined;
            }

            // Force options to be an object
            options = options || {};

            var // Create the final options object
                s = jQuery.extend( true, {}, jQuery.ajaxSettings, options ),
            // Callbacks contexts
            // We force the original context if it exists
            // or take it from jQuery.ajaxSettings otherwise
            // (plain objects used as context get extended)
                callbackContext =
                    ( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s,
                globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ),
            // Deferreds
                deferred = jQuery.Deferred(),
                completeDeferred = jQuery._Deferred(),
            // Status-dependent callbacks
                statusCode = s.statusCode || {},
            // Headers (they are sent all at once)
                requestHeaders = {},
            // Response headers
                responseHeadersString,
                responseHeaders,
            // transport
                transport,
            // timeout handle
                timeoutTimer,
            // Cross-domain detection vars
                loc = document.location,
                protocol = loc.protocol || "http:",
                parts,
            // The jXHR state
                state = 0,
            // Loop variable
                i,

            //假的XMLHttpRequest, 因为xml的属性 不兼容, 为xhr包裹一层, 方便统一管理;
            // Fake xhr
                jXHR = {

                    readyState: 0,

                    // Caches the header
                    setRequestHeader: function( name, value ) {
                        if ( state === 0 ) {
                            requestHeaders[ name.toLowerCase() ] = value;
                        }
                        return this;
                    },

                    // Raw string
                    getAllResponseHeaders: function() {
                        return state === 2 ? responseHeadersString : null;
                    },

                    // Builds headers hashtable if needed
                    getResponseHeader: function( key ) {
                        var match;
                        if ( state === 2 ) {
                            if ( !responseHeaders ) {
                                responseHeaders = {};
                                while( ( match = rheaders.exec( responseHeadersString ) ) ) {
                                    responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
                                }
                            }
                            match = responseHeaders[ key.toLowerCase() ];
                        }
                        return match || null;
                    },

                    // Cancel the request
                    abort: function( statusText ) {
                        statusText = statusText || "abort";
                        if ( transport ) {
                            transport.abort( statusText );
                        }
                        done( 0, statusText );
                        return this;
                    }
                };

            // Callback for when everything is done
            // It is defined here because jslint complains if it is declared
            // at the end of the function (which would be more logical and readable)
            function done( status, statusText, responses, headers) {

                // Called once
                if ( state === 2 ) {
                    return;
                }

                // State is "done" now
                state = 2;

                // Clear timeout if it exists
                if ( timeoutTimer ) {
                    clearTimeout( timeoutTimer );
                }

                // Dereference transport for early garbage collection
                // (no matter how long the jXHR object will be used)
                transport = undefined;

                // Cache response headers
                responseHeadersString = headers || "";

                // Set readyState
                jXHR.readyState = status ? 4 : 0;

                var isSuccess,
                    success,
                    error,
                    response = responses ? ajaxHandleResponses( s, jXHR, responses ) : undefined,
                    lastModified,
                    etag;

                // If successful, handle type chaining
                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 ) {

                        if ( ( lastModified = jXHR.getResponseHeader( "Last-Modified" ) ) ) {
                            jQuery.lastModified[ s.url ] = lastModified;
                        }
                        if ( ( etag = jXHR.getResponseHeader( "Etag" ) ) ) {
                            jQuery.etag[ s.url ] = etag;
                        };
                    };

                    // If not modified
                    if ( status === 304 ) {

                        statusText = "notmodified";
                        isSuccess = true;

                        // If we have data
                    } else {

                        try {
                            success = ajaxConvert( s, response );
                            statusText = "success";
                            isSuccess = true;
                        } catch(e) {
                            // We have a parsererror
                            statusText = "parsererror";
                            error = e;
                        };
                    };
                } else {
                    // We extract error from statusText
                    // then normalize statusText and status for non-aborts
                    error = statusText;
                    if( status ) {
                        statusText = "error";
                        if ( status < 0 ) {
                            status = 0;
                        }
                    }
                };

                // Set data for the fake xhr object
                jXHR.status = status;
                jXHR.statusText = statusText;

                // Success/Error
                if ( isSuccess ) {
                    debugger;
                    deferred.resolveWith( callbackContext, [ success, statusText, jXHR ] );
                } else {
                    deferred.rejectWith( callbackContext, [ jXHR, statusText, error ] );
                }

                // Status-dependent callbacks
                jXHR.statusCode( statusCode );
                statusCode = undefined;

                if ( s.global ) {
                    globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
                        [ jXHR, s, isSuccess ? success : error ] );
                }

                // Complete
                completeDeferred.resolveWith( callbackContext, [ jXHR, statusText ] );

                if ( s.global ) {
                    globalEventContext.trigger( "ajaxComplete", [ jXHR, s] );
                    // Handle the global AJAX counter
                    if ( !( --jQuery.active ) ) {
                        jQuery.event.trigger( "ajaxStop" );
                    }
                }
            };

            //var def = {}; $.Deferred().promise(def)   把这个对象送到promise会为着对象继承(非真正意义上的继承,只是复制了属性而已)一个延迟对象的实例;
            // Attach deferreds
            deferred.promise( jXHR );
            jXHR.success = jXHR.done;
            jXHR.error = jXHR.fail;
            jXHR.complete = completeDeferred.done;

            // Status-dependent callbacks
            jXHR.statusCode = function( map ) {
                if ( map ) {
                    var tmp;
                    if ( state < 2 ) {
                        for( tmp in map ) {
                            statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
                        }
                    } else {
                        tmp = map[ jXHR.status ];
                        jXHR.then( tmp, tmp );
                    }
                }
                return this;
            };
            //变量初始化完毕;

            // Remove hash character (#7531: and string promotion)
            // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
            // We also use the url parameter if available
            //去除空格;                 // rprotocol = /^\/\// 如果协议头是//就改成 ==》 当前协议头+//
            s.url = ( "" + ( url || s.url ) ).replace( rhash, "" ).replace( rprotocol, protocol + "//" );

            // Extract dataTypes list
            // rspacesAjax = /\s+/;
            //把用户期望的返回类型保存起来,方便回调;
            s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );

            //判断是否跨域了哇;
            // Determine if a cross-domain request is in order
            if ( !s.crossDomain ) {
                parts = rurl.exec( s.url.toLowerCase() );
                s.crossDomain = !!( parts &&
                    ( parts[ 1 ] != protocol || parts[ 2 ] != loc.hostname ||
                        ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                            ( loc.port || ( protocol === "http:" ? 80 : 443 ) ) )
                    );
            };

            // Convert data if not already a string
            if ( s.data && s.processData && typeof s.data !== "string" ) {
                //把json的数据转化成通过&拼接的数据;
                s.data = jQuery.param( s.data, s.traditional );
            };
            // Apply prefilters
            //prefilters = {JSON : fn,  JSONP : fn,  SCRIPT : fn};

            //根据setting的dataType类型设置预处理的参数;
            inspectPrefiltersOrTransports( prefilters, s, options, jXHR );

            // Uppercase the type
            s.type = s.type.toUpperCase();

            // Determine if request has content
            // /^(?:GET|HEAD)$/ 没有发送数据就是没有content;
            s.hasContent = !rnoContent.test( s.type );

            // Watch for a new set of requests
            //false
            // jQuery.active 目前等于 0;
            if ( s.global && jQuery.active++ === 0 ) {
                jQuery.event.trigger( "ajaxStart" );
            };

            // More options handling for requests with no content
            if ( !s.hasContent ) {

                // If data is available, append data to url
                if ( s.data ) {
                    // s.url  ==>>  "http://www.filltext.com/?callback=jQuery15003316175821237266_1420601952773"
                    // s.data ==>>  "callback=cb&rows=5&fname=%7BfirstName%7D&lname=%7BlastName%7D&tel=%7Bphone%7Cformat%7D"
                    s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
                    //jsonp会改变callback为jQ自己定义的callback然后在执行成功的时候才执行用户传进来的callback;
                    //最后变成了 ==>> http://www.filltext.com/?callback=jQuery15003316175821237266_1420601952773&callback=cb&rows=5&fname=%7BfirstName%7D&lname=%7BlastName%7D&tel=%7Bphone%7Cformat%7D
                };
                // Add anti-cache in url if needed
                if ( s.cache === false ) {

                    var ts = jQuery.now(),
                    // try replacing _= if it is there;
                    // rts = /([?&])_=[^&]*/; 把 ?_=替换成?=times 或者是 #_=替换成#_=times, 而且后面不是&, 避免出现别的错误;
                    /*
                     "sdfsdfdsf?_=abcd".replace(rts, "$1_="+jQuery.now())  ==>>  "sdfsdfdsf?_=1420614479567"
                     "sdfsdfdsf#_=abcd".replace(rts, "$1_="+jQuery.now())  ==>>  "sdfsdfdsf#_=abcd"
                     */
                        ret = s.url.replace( rts, "$1_=" + ts );

                    // 给最后添加一个时间戳;
                    // if nothing was replaced, add timestamp to the end
                    s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
                };
            };

            //JSON是不走这边的;
            // Set the correct header, if data is being sent
            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
                requestHeaders[ "content-type" ] = s.contentType;
            }

            //
            // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
            if ( s.ifModified ) {
                if ( jQuery.lastModified[ s.url ] ) {
                    requestHeaders[ "if-modified-since" ] = jQuery.lastModified[ s.url ];
                }
                if ( jQuery.etag[ s.url ] ) {
                    requestHeaders[ "if-none-match" ] = jQuery.etag[ s.url ];
                }
            }

            // 根据用户需求的请求头, 服务器可以返回期望的类型;
            // Set the Accepts header for the server, depending on the dataType
            requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
                s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
                s.accepts[ "*" ];

            // Check for headers option
            for ( i in s.headers ) {
                requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
            };
            // 执行before的事件,这个是全局的;
            // Allow custom headers/mimetypes and early abort
            if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jXHR, s ) === false || state === 2 ) ) {
                // Abort if not done already
                done( 0, "abort" );
                // Return false
                jXHR = false;

            } else {

                // Install callbacks on deferreds
                for ( i in { success: 1, error: 1, complete: 1 } ) {
                    //把用户的回调安装到延迟对象;
                    //jXHR.success( s.success );
                    //jXHR.complete( s.complete );
                    //jXHR.error( s.error );
                    jXHR[ i ]( s[ i ] );
                }

                // Get transport
                //获取传送器, 根据传送器的设置发送数据, 这个里面会执行get,post或者新增script的操作;
                transport = inspectPrefiltersOrTransports( transports, s, options, jXHR );

                // If no transport, we auto-abort
                if ( !transport ) {
                    done( -1, "No Transport" );
                } else {
                    // Set state as sending
                    state = jXHR.readyState = 1;
                    // Send global event
                    // 触发全局的ajaxSend事件;参数为JXHR, s;
                    if ( s.global ) {
                        globalEventContext.trigger( "ajaxSend", [ jXHR, s ] );
                    }

                    //开启一定定时器,如果是异步而且超时的话就触发超时的事件;
                    // Timeout
                    if ( s.async && s.timeout > 0 ) {
                        timeoutTimer = setTimeout( function(){
                            jXHR.abort( "timeout" );
                        }, s.timeout );
                    }

                    //JSONP的传送器有一个是send,一个是abort;
                    try {
                        transport.send( requestHeaders, done );
                    } catch (e) {
                        //如果出了错;
                        // Propagate exception as error if not done
                        if ( status < 2 ) {
                            done( -1, e );
                            // Simply rethrow otherwise
                        } else {
                            jQuery.error( e );
                        }
                    }
                }
            }
            return jXHR;
        },

        // Serialize an array of form elements or a set of
        // key/values into a query string
        param: function( a, traditional ) {
            var s = [],
                add = function( key, value ) {
                    // If value is a function, invoke it and return its value
                    value = jQuery.isFunction( value ) ? value() : value;
                    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
                };

            // Set traditional to true for jQuery <= 1.3.2 behavior.
            if ( traditional === undefined ) {
                traditional = jQuery.ajaxSettings.traditional;
            }

            // If an array was passed in, assume that it is an array of form elements.
            if ( jQuery.isArray( a ) || a.jquery ) {
                // Serialize the form elements
                jQuery.each( a, function() {
                    add( this.name, this.value );
                } );

            } else {
                // If traditional, encode the "old" way (the way 1.3.2 or older
                // did it), otherwise encode params recursively.
                for ( var prefix in a ) {
                    buildParams( prefix, a[ prefix ], traditional, add );
                }
            }

            // Return the resulting serialization
            return s.join( "&" ).replace( r20, "+" );
        }
    });

    function buildParams( prefix, obj, traditional, add ) {
        if ( jQuery.isArray( obj ) && obj.length ) {
            // Serialize array item.
            jQuery.each( obj, function( i, v ) {
                if ( traditional || rbracket.test( prefix ) ) {
                    // Treat each array item as a scalar.
                    add( prefix, v );

                } else {
                    // If array item is non-scalar (array or object), encode its
                    // numeric index to resolve deserialization ambiguity issues.
                    // Note that rack (as of 1.0.0) can't currently deserialize
                    // nested arrays properly, and attempting to do so may cause
                    // a server error. Possible fixes are to modify rack's
                    // deserialization algorithm or to provide an option or flag
                    // to force array serialization to be shallow.
                    buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
                }
            });

        } else if ( !traditional && obj != null && typeof obj === "object" ) {
            // If we see an array here, it is empty and should be treated as an empty
            // object
            if ( jQuery.isArray( obj ) || jQuery.isEmptyObject( obj ) ) {
                add( prefix, "" );

                // Serialize object item.
            } else {
                jQuery.each( obj, function( k, v ) {
                    buildParams( prefix + "[" + k + "]", v, traditional, add );
                });
            }

        } else {
            // Serialize scalar item.
            add( prefix, obj );
        }
    }

// This is still on the jQuery object... for now
// Want to move this to jQuery.ajax some day
    jQuery.extend({

        // Counter for holding the number of active queries
        active: 0,

        // Last-Modified header cache for next request
        lastModified: {},
        etag: {}

    });

    /* 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
     */
    function ajaxHandleResponses( s, jXHR, responses ) {

        var contents = s.contents,
            dataTypes = s.dataTypes,
            responseFields = s.responseFields,
            ct,
            type,
            finalDataType,
            firstDataType;

        // Fill responseXXX fields
        for( type in responseFields ) {
            if ( type in responses ) {
                jXHR[ responseFields[type] ] = responses[ type ];
            }
        }

        // Remove auto dataType and get content-type in the process
        while( dataTypes[ 0 ] === "*" ) {
            dataTypes.shift();
            if ( ct === undefined ) {
                ct = jXHR.getResponseHeader( "content-type" );
            }
        }

        // Check if we're dealing with a known content-type
        if ( ct ) {
            for ( type in contents ) {
                if ( contents[ type ] && contents[ type ].test( ct ) ) {
                    dataTypes.unshift( type );
                    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
            for ( type in responses ) {
                if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
                    finalDataType = type;
                    break;
                }
                if ( !firstDataType ) {
                    firstDataType = type;
                }
            }
            // Or just use first one
            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 ] ) {
                dataTypes.unshift( finalDataType );
            }
            return responses[ finalDataType ];
        }
    }

// Chain conversions given the request and the original response
    function ajaxConvert( s, response ) {

        // Apply the dataFilter if provided
        if ( s.dataFilter ) {
            response = s.dataFilter( response, s.dataType );
        }

        var dataTypes = s.dataTypes,
            converters = s.converters,
            i,
            length = dataTypes.length,
            tmp,
        // Current and previous dataTypes
            current = dataTypes[ 0 ],
            prev,
        // Conversion expression
            conversion,
        // Conversion function
            conv,
        // Conversion functions (transitive conversion)
            conv1,
            conv2;

        // For each dataType in the chain
        for( i = 1; i < length; i++ ) {

            // Get the dataTypes
            prev = current;
            current = dataTypes[ i ];

            // If current is auto dataType, update it to prev
            if( current === "*" ) {
                current = prev;
                // If no auto and dataTypes are actually different
            } else if ( prev !== "*" && prev !== current ) {

                // Get the converter
                conversion = prev + " " + current;
                conv = converters[ conversion ] || converters[ "* " + current ];

                // If there is no direct converter, search transitively
                if ( !conv ) {
                    conv2 = undefined;
                    for( conv1 in converters ) {
                        tmp = conv1.split( " " );
                        if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
                            conv2 = converters[ tmp[1] + " " + current ];
                            if ( conv2 ) {
                                conv1 = converters[ conv1 ];
                                if ( conv1 === true ) {
                                    conv = conv2;
                                } else if ( conv2 === true ) {
                                    conv = conv1;
                                }
                                break;
                            }
                        }
                    }
                }
                // If we found no converter, dispatch an error
                if ( !( conv || conv2 ) ) {
                    jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
                }
                // If found converter is not an equivalence
                if ( conv !== true ) {
                    // Convert with 1 or 2 converters accordingly
                    response = conv ? conv( response ) : conv2( conv1(response) );
                }
            }
        }
        return response;
    }




    var jsc = jQuery.now(),
        jsre = /(\=)\?(&|$)|()\?\?()/i;

// Default jsonp settings
    jQuery.ajaxSetup({
        jsonp: "callback",
        jsonpCallback: function() {
            return jQuery.expando + "_" + ( jsc++ );
        }
    });

// Detect, normalize options and install callbacks for jsonp requests
    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, dataIsString /* internal */ ) {

        dataIsString = ( typeof s.data === "string" );

        if ( s.dataTypes[ 0 ] === "jsonp" ||
            originalSettings.jsonpCallback ||
            originalSettings.jsonp != null ||
            s.jsonp !== false && ( jsre.test( s.url ) ||
                dataIsString && jsre.test( s.data ) ) ) {

            var responseContainer,
                jsonpCallback = s.jsonpCallback =
                    jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
                previous = window[ jsonpCallback ],
                url = s.url,
                data = s.data,
                replace = "$1" + jsonpCallback + "$2";

            if ( s.jsonp !== false ) {
                url = url.replace( jsre, replace );
                if ( s.url === url ) {
                    if ( dataIsString ) {
                        data = data.replace( jsre, replace );
                    }
                    if ( s.data === data ) {
                        // Add callback manually
                        url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
                    }
                }
            }

            s.url = url;
            s.data = data;

            window[ jsonpCallback ] = function( response ) {
                responseContainer = [ response ];
            };

            s.complete = [ function() {

                // Set callback back to previous value
                window[ jsonpCallback ] = previous;

                // Call if it was a function and we have a response
                if ( previous) {
                    if ( responseContainer && jQuery.isFunction( previous ) ) {
                        window[ jsonpCallback ] ( responseContainer[ 0 ] );
                    }
                } else {
                    // else, more memory leak avoidance
                    try{
                        delete window[ jsonpCallback ];
                    } catch( e ) {}
                }

            }, s.complete ];

            // Use data converter to retrieve json after script execution
            s.converters["script json"] = function() {
                if ( ! responseContainer ) {
                    jQuery.error( jsonpCallback + " was not called" );
                }
                return responseContainer[ 0 ];
            };

            // force json dataType
            s.dataTypes[ 0 ] = "json";

            // Delegate to script
            return "script";
        }
    } );




// Install script dataType
    jQuery.ajaxSetup({
        accepts: {
            script: "text/javascript, application/javascript"
        },
        contents: {
            script: /javascript/
        },
        converters: {
            "text script": function( text ) {
                jQuery.globalEval( text );
                return text;
            }
        }
    });

// Handle cache's special case and global
    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
    //这个是跨域的传送器哇;
    jQuery.ajaxTransport( "script", function(s) {

        // This transport only deals with cross domain requests
        // 如果请求的url发生改变,就默认用jsonp方式(script标签)进行加载;
        if ( s.crossDomain ) {

            var script,
                head = document.getElementsByTagName( "head" )[ 0 ] || document.documentElement;

            return {

                send: function( _, callback ) {

                    script = document.createElement( "script" );
                    //async是标准浏览器所支持的东西;
                    //defer是IE支持的异步加载方式;
                    script.async = "async";

                    //字符串编码;
                    if ( s.scriptCharset ) {
                        script.charset = s.scriptCharset;
                    }

                    //script和link标签只有在添加到dom才发送请求哇;
                    script.src = s.url;

                    // Attach handlers for all browsers
                    // 事件还是先加载
                    script.onload = script.onreadystatechange = function( _, isAbort ) {

                        if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {

                            // Handle memory leak in IE
                            // IE的内存泄露问题;
                            script.onload = script.onreadystatechange = null;

                            // Remove the script
                            if ( head && script.parentNode ) {
                                head.removeChild( script );
                            }

                            // Dereference the script
                            script = undefined;

                            //因为是JSONP的方式, 就直接返回200的状态和 success的姿态;
                            // Callback if not abort
                            if ( !isAbort ) {
                                callback( 200, "success" );
                            }
                        }
                    };
                    // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
                    // This arises when a base node is used (#2709 and #4378).
                    head.insertBefore( script, head.firstChild );
                },

                abort: function() {
                    if ( script ) {
                        script.onload( 0, 1 );
                    }
                }
            };
        }
    } );




    var // Next active xhr id
        xhrId = jQuery.now(),

    // active xhrs
        xhrs = {},

    // #5280: see below
        xhrUnloadAbortInstalled,
    // 用来临时用的;
    // XHR used to determine supports properties
        testXHR;

// 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.
         */
        function() {
            if ( window.location.protocol !== "file:" ) {
                try {
                    return new window.XMLHttpRequest();
                } catch( xhrError ) {}
            }

            try {
                return new window.ActiveXObject("Microsoft.XMLHTTP");
            } catch( activeError ) {}
        } :
        // For all other browsers, use the standard XMLHttpRequest object
        function() {
            return new window.XMLHttpRequest();
        };

// Test if we can create an xhr object
    try {
        testXHR = jQuery.ajaxSettings.xhr();
    } catch( xhrCreationException ) {};

    //测试是否支持XHR这个东西;
    //Does this browser support XHR requests?
    jQuery.support.ajax = !!testXHR;

    /*
     默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。
     通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。
     如果服务器接收带凭据的请求,会用下面的HTTP头部来响应。
     如果发送的是带凭据的请求,但服务器的相应中没有包含这个头部,
     那么浏览器就不会把相应交给JavaScript(于是,responseText中将是空字符串,status的值为0,
     而且会调用onerror()事件处理程序)。
     另外,服务器还可以在Preflight响应中发送这个HTTP头部,
     表示允许源发送带凭据的请求。
     支持withCredentials属性的浏览器有Firefox 3.5+、Safari 4+和Chrome。IE10及更早版本都不支持。
     */
    // 是否支持跨域直接通过withCredentials进行判断;
    // Does this browser support crossDomain XHR requests
    jQuery.support.cors = testXHR && ( "withCredentials" in testXHR );

    // No need for the temporary xhr anymore
    testXHR = undefined;

    // Create transport if the browser can provide an xhr
    if ( jQuery.support.ajax ) {
        //传进来的是setting;
        jQuery.ajaxTransport(function( s ) {
            // Cross domain only allowed if supported through XMLHttpRequest
            if ( !s.crossDomain || jQuery.support.cors ) {

                var callback;

                return {
                    send: function( headers, complete ) {

                        //避免错误的
                        // #5280: we need to abort on unload or IE will keep connections alive
                        if ( !xhrUnloadAbortInstalled ) {

                            xhrUnloadAbortInstalled = 1;

                            jQuery(window).bind( "unload", function() {

                                // Abort all pending requests
                                jQuery.each( xhrs, function( _, xhr ) {
                                    if ( xhr.onreadystatechange ) {
                                        xhr.onreadystatechange( 1 );
                                    }
                                } );

                            } );
                        }

                        // Get a new xhr
                        var xhr = s.xhr(),
                            handle;

                        // 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 );
                        }

                        // Requested-With header
                        // Not set for crossDomain requests with no content
                        // (see why at http://trac.dojotoolkit.org/ticket/9486)
                        // Won't change header if already provided
                        // 设置头;
                        if ( !( s.crossDomain && !s.hasContent ) && !headers["x-requested-with"] ) {
                            headers[ "x-requested-with" ] = "XMLHttpRequest";
                        }

                        // Need an extra try/catch for cross domain requests in Firefox 3
                        try {
                            jQuery.each( headers, function( key, value ) {
                                xhr.setRequestHeader( key, value );
                            } );
                        } catch( _ ) {};

                        // Do send the request
                        // This may raise an exception which is actually
                        // handled in jQuery.ajax (so no try/catch here)
                        // POST或者是GET,所以要判断一下;
                        xhr.send( ( s.hasContent && s.data ) || null );

                        //cancel when I use arguments (0,1 ), kind like : callback(0,1);
                        // Listener
                        callback = function( _, isAbort ) {

                            // Was never called and is aborted or complete
                            if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
                                //执行成功了就不用了;
                                // Only called once
                                callback = 0;

                                // Do not keep as active anymore
                                if ( handle ) {
                                    xhr.onreadystatechange = jQuery.noop;
                                    delete xhrs[ handle ];
                                }

                                // If it's an abort
                                //cance和send放在一起, 一个接口, 多个使用;
                                if ( isAbort ) {
                                    // Abort it manually if needed
                                    if ( xhr.readyState !== 4 ) {
                                        xhr.abort();
                                    }
                                } else {
                                    // Get info
                                    var status = xhr.status,
                                        statusText,
                                        responseHeaders = xhr.getAllResponseHeaders(),
                                        responses = {},
                                        xml = xhr.responseXML;

                                    // Construct response list
                                    if ( xml && xml.documentElement /* #4958 */ ) {
                                        responses.xml = xml;
                                    }
                                    responses.text = xhr.responseText;

                                    // Firefox throws an exception(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 behaviours    
                                    status =
                                        // Opera returns 0 when it should be 304
                                        // Webkit returns 0 for failing cross-domain no matter the real status
                                        status === 0 ?
                                            (
                                                // Webkit, Firefox: filter out faulty cross-domain requests
                                                !s.crossDomain || statusText ?
                                                    (
                                                        // Opera: filter out real aborts #6060
                                                        responseHeaders ?
                                                            304 :
                                                            0
                                                        ) :
                                                    // We assume 302 but could be anything cross-domain related
                                                    302
                                                ) :
                                            (
                                                // IE sometimes returns 1223 when it should be 204 (see #1450)
                                                status == 1223 ?
                                                    204 :
                                                    status
                                                );

                                    // Call complete
                                    //有responseXML和
                                    //responseText两种值;
                                    //返回的返回头;
                                    complete( status, statusText, responses, responseHeaders );
                                }
                            }
                        };

                        // if we're in sync mode or it's in cache
                        // and has been retrieved directly (IE6 & IE7)
                        // we need to manually fire the callback
                        if ( !s.async || xhr.readyState === 4 ) {
                            callback();
                        } else {
                            // Add to list of active xhrs
                            handle = xhrId++;
                            xhrs[ handle ] = xhr;
                            xhr.onreadystatechange = callback;
                        }
                    },

                    abort: function() {
                        if ( callback ) {
                            callback(0,1);
                        }
                    }
                };
            }
        });
    }
View Code

相关文章:

  • 2022-12-23
  • 2021-08-15
  • 2022-12-23
  • 2021-12-09
  • 2021-09-25
  • 2021-07-11
  • 2022-12-23
  • 2021-12-11
猜你喜欢
  • 2022-12-23
  • 2021-12-05
  • 2022-12-23
  • 2021-08-08
  • 2021-12-27
  • 2021-07-16
  • 2022-12-23
相关资源
相似解决方案