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