jQuery原始碼分析之$.ajax方法
針對獲取到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;
}
note:如果通過location.href獲取地址出錯,那麼我們就通過建立A標籤,然後獲取該標籤的href!在IE中可以列印主機名,如"http://locahost:8080/"關於去除URL中的hash值,同時相容IE7,如果沒有協議欄位我們要手動新增:
匹配是否跨域請求的部分:var ajaxLocation="http://localhost:8080/qinl/a.action?#23" var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/; var rhash = /#.*$/; //匹配開頭的"//"欄位 var rprotocol = /^\/\//; //獲取前面的協議欄位,如"http:","file:"等 var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; //第一個replace去掉用hash值,第二個replace表示如果去掉hash值以後開頭已經是//那麼也要進行相應的處理 var result=ajaxLocation.replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
//測試程式碼1: var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/; //列印[http://localhost:8080,http:,localhost,8080] alert(rurl.exec("http://localhost:8080/qinl/xx.action")); //測試程式碼2: //http://www.365mini.com/diy.php?f=jquery_ajax-demo var ajaxLocation=location.href; var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; //列印[http://www.365mini.com,http:,www.365mini.com,] alert(ajaxLocParts);
首先來一段精簡版的$.ajax方法:也就是其中的原理
var completeDeferred = jQuery.Callbacks("once memory"); var dfd=new $.Deferred(); var jqXHR={ } //jqXHR有了promise所有方法,但是修改狀態還是要靠Deferred物件! //同時為這個jqXHR封裝一個Callbacks物件,呼叫complete就想相當於向其中新增 //回撥函式,然後要觸發他就直接呼叫fireWith才行! dfd.promise(jqXHR).complete=completeDeferred.add; var f1=function() { alert("f1"); } //為done和complete添加回調函式 jqXHR.done(f1).complete(f1); //呼叫fire觸發所有的complete新增的回撥 completeDeferred.fire(); //觸發Deferred物件的所有的done集合中函式,記住這裡 //不能是dfd呼叫,因為他沒有resolve方法不能改變狀態! dfd.resolve();
ajax原始碼分析:
ajax: function( url, options ) {
//ajax方法引數調整,如果url是object,這是我們一般的呼叫方式
// If url is an object, simulate pre-1.5 signature
if ( typeof url === "object" ) {
options = url;
url = undefined;
}
//option是一個物件
// 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
s = jQuery.ajaxSetup( {}, options ),
// Callbacks context
//設定context,如果沒有context那麼就是返回的最終的options=ajaxSettings+options(使用者呼叫ajax方法時候傳送的option)
callbackContext = s.context || s,
//如果傳入的物件有context,同時context是DOM物件或者是jQuery物件,那麼把該DOM物件封裝為jQuery物件
//如果不滿足也就是沒有context或者context不是DOM物件和jQuery物件,那麼globalEventContext就是jQuery.event物件!
// Context for global events is callbackContext if it is a DOM node or jQuery collection
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
jQuery( callbackContext ) :
jQuery.event,
//建立Deferred物件
// Deferreds
deferred = jQuery.Deferred(),
//建立Callbacks物件
completeDeferred = jQuery.Callbacks("once memory"),
//獲取最終options的statusCode引數,預設是空物件!
// Status-dependent callbacks
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
// The jqXHR state
state = 0,
// Default abort message
strAbort = "canceled",
//建立一個偽的xhr物件,該物件有readyState,getResponseHeader,getAllResponseHeaders,setRequestHeader
//overrideMimeType,statusCode,abort方法和屬性!
// Fake xhr
jqXHR = {
readyState: 0,
// Builds headers hashtable if needed
getResponseHeader: function( key ) {
var match;
//狀態是2的時候才能獲取資料
if ( state === 2 ) {
if ( !responseHeaders ) {
responseHeaders = {};
//rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
//responseHeaders的鍵名就是第一個捕獲組的資料,第二個鍵值就是第二個捕獲組資料!
while ( (match = rheaders.exec( responseHeadersString )) ) {
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
}
}
//返回這個key對應的鍵值!
match = responseHeaders[ key.toLowerCase() ];
}
return match == null ? null : match;
},
// Raw string
//如果狀態是2,那麼就是responseHeadersString
getAllResponseHeaders: function() {
return state === 2 ? responseHeadersString : null;
},
// Caches the header
//設定HTTP請求頭的時候,頭是小寫的
setRequestHeader: function( name, value ) {
var lname = name.toLowerCase();
//如果state為0那麼就快取頭,把結果放入requestHeaders中!但是要提前查詢requestHeadersNames
if ( !state ) {
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
requestHeaders[ name ] = value;
}
return this;
},
// Overrides response content-type header
//如果state=0那麼可以覆蓋這個mimetype!
overrideMimeType: function( type ) {
if ( !state ) {
s.mimeType = type;
}
return this;
},
// Status-dependent callbacks
statusCode: function( map ) {
var code;
if ( map ) {
if ( state < 2 ) {
for ( code in map ) {
// Lazy-add the new callback in a way that preserves old ones
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
}
} else {
// Execute the appropriate callbacks
jqXHR.always( map[ jqXHR.status ] );
}
}
return this;
},
// Cancel the request
//取消請求
abort: function( statusText ) {
var finalText = statusText || strAbort;
if ( transport ) {
transport.abort( finalText );
}
done( 0, finalText );
return this;
}
};
// Attach deferreds
//讓jqXHR具有promise的所有的屬性和方法!不包括狀態改變的方法,如resollveWith等
//同時jqXHR的complete對應於completeDeferred的add方法,但是該jqXHR中也封裝了三個Callbacks物件
//但是這裡沒有用內部的Callbacks物件,而是採用一個新的Callbacks物件
//completeDeferred = jQuery.Callbacks("once memory"),
deferred.promise( jqXHR ).complete = completeDeferred.add;
//success呼叫的promise物件內建的done方法對應於的Callbacks物件
jqXHR.success = jqXHR.done;
//error方法呼叫的promise物件內建的fail方法對應的Callbacks物件!
//注意:這個內建的promise物件的progress方法對應的Callbacks物件沒有用到!
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
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
//type就是get或者post。這個方式可以通過使用者傳入的物件的method或者type或者最終的物件的method或者type獲取!
// Alias method option to type as per ticket #12004
s.type = options.method || options.type || s.method || s.type;
// Extract dataTypes list
//取出dataType兩邊的空格,同時通過空格進行分組得到一個數組!dataType="html"
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
//如果沒有crossDomain物件
// 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() );
//如果在同一個域名裡面那麼這裡的判斷都是false,結果就是crossDomain為false
//如果不再同一個域名裡面,那麼這裡的判斷都是true,結果就是crossDomain為true!
s.crossDomain = !!( parts &&
( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
);
}
//如果存在data同時存在processData同時data不是string!
//traditional是為了相容jQuery<1.3.2行為的!
// Convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
}
// Apply prefilters
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
//如果在預過濾器中已經終止了請求,那麼直接返回jqXHR物件!
// 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
// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
//如果是global引數,那麼我們直接trigger事件ajaxStart!
fireGlobals = jQuery.event && s.global;
// Watch for a new set of requests
if ( fireGlobals && jQuery.active++ === 0 ) {
jQuery.event.trigger("ajaxStart");
}
// Uppercase the type
//把type變成大寫
s.type = s.type.toUpperCase();
// Determine if request has content
//rnoContent = /^(?:GET|HEAD)$/
//也就是如果沒有指定type也就是請求方式!
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
//獲取url引數!
cacheURL = s.url;
// More options handling for requests with no content
//如果指定了請求方式,如get,post等!
if ( !s.hasContent ) {
//沒有指定請求方式的時候有傳遞資料!
// If data is available, append data to url
if ( s.data ) {
//var rquery = (/\?/);
//如果url後面有問號,那麼直接把引數繫結到問號後面就可以了!否則新增問號在繫結!
//同時刪除資料!
cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Add anti-cache in url if needed
//如果指定了cache為false表示不能進行資料快取,那麼會在url後面新增一個當前時間!
if ( s.cache === false ) {
s.url = rts.test( cacheURL ) ?
// If there is already a '_' parameter, set its value
//var nonce = jQuery.now();
cacheURL.replace( rts, "$1_=" + nonce++ ) :
// Otherwise add one to the end
cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
}
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
//如果添加了ifModified頭
//var lastModified={}
if ( s.ifModified ) {
//如果lastModified儲存了這個cacheURL也就是這個URL有快取了!那麼直接新增頭If-Modified-Since資料為
//jQuery.lastModified[ cacheURL ]獲取到的資料!
if ( jQuery.lastModified[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
}
//如果在etag: {}中儲存了這個URL
//那麼新增If-None-Match,因為Etag和if-None-Match是一對,Last-Modified和If-Modified-Since是一對!
if ( jQuery.etag[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
}
}
// Set the correct header, if data is being sent
//如果有資料傳送,同時也指定了get,post方法,同時contentType也指定!
//那麼新增一個頭Content-Type!
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
//(1)如果指定了dataType,同時accepts中dataType存在,也就是必須是指定的data[type]
jqXHR.setRequestHeader(
"Accept",
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
// allTypes = "*/".concat("*");
//如果支援的資料型別是內建的型別,那麼獲取內建的值,如text獲取的就是"text/plain"
//同時dataTypes[0]不是"*",那麼我們加上一個逗號,同時加上後面剩餘的部分!
/*
var allTypes = "*/".concat("*");
//列印
//alert(allTypes);
//最後的格式就是:text/html,*/*;q=0.01
//如果傳入的dataType就是*,那麼最後的結果就是"*/*"
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
s.accepts[ "*" ]
);
// Check for headers option
//如果還定義了headers選項,那麼會被逐個傳送到伺服器端!
for ( i in s.headers ) {
jqXHR.setRequestHeader( i, s.headers[ i ] );
}
// Allow custom headers/mimetypes and early abort
//如果指定了beforeSend,同時beforeSend的函式呼叫的結果是false或者state是2,那麼取消這次請求
//beforeSend中傳入的引數為callbackContext = s.context || s也就是最終物件的context引數為上下文
//第一個引數是XHR物件,第二個引數是最終的options物件!
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
//往jqXHR["success"]和jqXHR["error"],jqXHR["complete"]中添加回調函式
//回撥函式就是通過最終options物件獲取到的success,error,complete函式!
for ( i in { success: 1, error: 1, complete: 1 } ) {
jqXHR[ i ]( s[ i ] );
}
// Get transport
//傳入的引數是transports物件!這個函式裡面會判斷是否傳入了transports
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
// If no transport, we auto-abort
if ( !transport ) {
done( -1, "No Transport" );
} else {
//如果有transport那麼readyState就是1,表示 (載入)已呼叫send()方法,正在傳送請求,也就是請求的傳送是在
//inspectPrefiltersOrTransports裡面完成的!
jqXHR.readyState = 1;
// Send global event
//指示是否觸發全域性Ajax事件。將該值設為false將阻止全域性事件處理函式被觸發
//fireGlobals = jQuery.event && s.global;
//如果是表示全域性ajax事件,那麼我們要呼叫ajaxSend方法!同時為這個方法傳入引數jqXHR和最終option!
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
}
// Timeout
//如果指定了async同時timeout>0表示指定了隔多少秒就放棄
//一個超時呼叫,超時直接呼叫abort方法!
if ( s.async && s.timeout > 0 ) {
timeoutTimer = setTimeout(function() {
jqXHR.abort("timeout");
}, s.timeout );
}
//如果有transport,那麼呼叫send方法!
try {
state = 1;
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;
}
}
}
總結:
(1)呼叫 jQuery.ajaxSetup( {}, options )讓最終options具有jQuery內建的所有的屬性,同時也包括使用者呼叫ajax方法的時候傳入的所有的屬性和方法!
(2)建立jqXHR物件,讓該物件具有Deferred的所有屬性和方法,該Deferred物件可以繫結使用者的success和error方法。但是使用者傳入的compelte方法表示任何情況下都會呼叫,我們就引入了一個Callbacks物件,把complete回撥函式存入該Callback中(用fireWith呼叫)
(3)對URL處理,取出hash加上協議名稱,為type賦值,也就是Get/Post方法(使用者可以通過method或者type傳入該方法);指定dataTypes說明使用者需要要的資料型別(使用者通過dataType傳入);如果使用者沒有明確指定crossDomain,那麼自己判斷,如果使用者傳入的URL也就是訪問的URL和當前的location.href不相同(包括協議名稱,主機名,埠號等),那麼直接把crossDomain設定為true;如果傳入了資料,也就是data那麼通過 jQuery.param方法把這個資料序列化;
(4)上述步驟完成以後,我們就呼叫inspectPrefiltersOrTransports,這個方法傳入了prefilters,表示對prefilters中所有的預處理函式進行檢測,該方法可以修改前面所有的引數,當然也可以新增新的資訊!(這裡是prefilters)
(5)如果使用者傳入了global引數,那麼我們在這個步驟執行"ajaxStart"事件
globalBoolean型別
預設值:true
。指示是否觸發全域性Ajax事件。將該值設為false
將阻止全域性事件處理函式被觸發,例如ajaxStart()和ajaxStop()。它可以用來控制各種Ajax事件。
(7)如果使用者指定了ifModified,表示只有資料改變時候才傳送請求。如果這個URL已經訪問過了,那麼我們取出訪問該URL時候伺服器給的etag和if-none-match標籤,並且把他們通過"If-None-Match和If-Modified-Since形式傳送給伺服器端,讓伺服器端去判斷資料有沒有改變。這兩個頭是在done方法中,也就是成功回撥時候設定的!
ifModifiedBoolean型別
預設值:false
。允許當前請求僅在伺服器資料改變時獲取新資料(如未更改,瀏覽器從快取中獲取資料)。它使用HTTP頭資訊Last-Modified
來判斷。從jQuery
1.4開始,他也會檢查伺服器指定的'etag'來確定資料是否已被修改。
contentTypeString型別
預設值:'application/x-www-form-urlencoded; charset=UTF-8'。使用指定的內容編碼型別將資料傳送給伺服器。W3C的XMLHttpRequest規範規定charset始終是UTF-8,你如果將其改為其他字符集,也無法強制瀏覽器改字元編碼。
(9)設定accept頭,告訴伺服器瀏覽器能夠接受的資料型別acceptsObject型別
預設值:取決於dataType
屬性。傳送的內容型別請求頭,用於告訴伺服器——瀏覽器可以接收伺服器返回何種型別的響應。如果傳入的是"*"結果就是"*/*",否則就是如格式"text/html,*/*;q=0.01"
headersObject型別1.5 新增
預設值:{}
。以物件形式指定附加的請求頭資訊。請求頭X-Requested-With:
XMLHttpRequest
將始終被新增,當然你也可以在此處修改預設的XMLHttpRequest值。headers
中的值可以覆蓋beforeSend
回撥函式中設定的請求頭(意即beforeSend先被呼叫)。
beforeSendFunction型別
指定在請求傳送前需要執行的回撥函式。該函式還有兩個引數:其一是jqXHR
物件,其二是當前settings
物件。這是一個Ajax事件,如果該函式返回false
,將取消本次ajax請求。
(13)這一步是重點:呼叫transport裡面所有的函式集合。函式呼叫的返回結果是一個物件,該物件有send和abort方法。呼叫send方法就是真正的向伺服器傳送資料,如果沒有得到transport物件那麼表示請求失敗。如果得到了這個物件,那麼我們把readyState設定為1,然後呼叫send方法,但是呼叫send方法之前我們要呼叫ajaxSend方法!在send方法呼叫時候transport.send( requestHeaders, done );我們傳入了回撥函式done方法,該方法處理了回撥的邏輯!
我們看看下面的done方法的處理邏輯:
function done( status, nativeStatusText, responses, headers ) {
var isSuccess, success, error, response, modified,
statusText = nativeStatusText;
// Called once
//如果state是2,那麼直接返回!
if ( state === 2 ) {
return;
}
// State is "done" now
//state設定為2表示不會再次執行了!
state = 2;
// Clear timeout if it exists
//如果timeoutTimer存在,那麼直接清除!
if ( timeoutTimer ) {
clearTimeout( timeoutTimer );
}
// Dereference transport for early garbage collection
// (no matter how long the jqXHR object will be used)
transport = undefined;
// Cache response headers
//獲取response的頭部資訊,預設是空!
responseHeadersString = headers || "";
// Set readyState
//如果status>0那麼把readyState設定為4!
jqXHR.readyState = status > 0 ? 4 : 0;
// Determine if successful
//如果status在指定的區間內那麼表示成功!
isSuccess = status >= 200 && status < 300 || status === 304;
// Get response data
//如果done方法有responses那麼對他進行處理!
if ( responses ) {
response = ajaxHandleResponses( s, jqXHR, responses );
}
// Convert no matter what (that way responseXXX fields are always set)
response = ajaxConvert( s, response, jqXHR, isSuccess );
// If successful, handle type chaining
if ( isSuccess ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
//如果ifModified存在,那麼就要設定If-Modified-Since和If-None-Match頭!
if ( s.ifModified ) {
modified = jqXHR.getResponseHeader("Last-Modified");
if ( modified ) {
jQuery.lastModified[ cacheURL ] = modified;
}
modified = jqXHR.getResponseHeader("etag");
if ( modified ) {
jQuery.etag[ cacheURL ] = modified;
}
}
// if no content
//204表示沒有資料,這時候頁面就不需要跳轉!還是停留在當前頁面!
if ( status === 204 || s.type === "HEAD" ) {
statusText = "nocontent";
//如果是304那麼表示沒有修改內容!
// if not modified
} else if ( status === 304 ) {
statusText = "notmodified";
//如果有資料,那麼我們獲取到資料!
// If we have data, let's convert it
} else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
//這裡的else表示請求失敗!我們從statusText獲取到錯誤的資訊,然後對statusText進行處理!
// 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;
}
}
}
//為jqXHR設定資料
// Set data for the fake xhr object
jqXHR.status = status;
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
// Success/Error
if ( isSuccess ) {
//如果成功了請求,那麼我們傳入的Context是callbackContext,傳入的資料是response.data
//response.status和jqXHR物件
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
} else {
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
}
// Status-dependent callbacks
jqXHR.statusCode( statusCode );
statusCode = undefined;
//如果是全域性執行
if ( fireGlobals ) {
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
[ jqXHR, s, isSuccess ? success : error ] );
}
// Complete
//這個物件新增的所有函式執行,表示完成,不是成功,失敗,而是complelte表示不管成功與否都是會執行的!
//而且只會執行一次!
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
if ( fireGlobals ) {
//globalEventContext也就是最終options的事件,觸發事件ajaxComplete!
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
// Handle the global AJAX counter
//如果全域性的ajax計數器已經是0了,那麼就會觸發ajaxStrop事件!
if ( !( --jQuery.active ) ) {
jQuery.event.trigger("ajaxStop");
}
}
}
return jqXHR;
}
});
note:
(1)state在send呼叫之前為1,在done方法呼叫的時候設定為2,預設為0.所以2表示已經回撥成功了,1表示send方法已經呼叫但是還沒有回撥。
(2)呼叫順序是ajaxStart,ajaxSend,ajaxSuccess/ajaxError,ajaxComplete,ajaxStop這就是全域性事件的呼叫順序!
(3)在done方法中通過resolveWith,rejectWith來觸發success,error事件,通過fireWith來觸發compelete事件
(4)返回真實的伺服器端資料,如responseText伺服器端的資料!ajaxHandleResponses作用:把伺服器端返回的資料封裝到jqXHR物件上面,形成jqXHR["responseText"]=xhr.responseText這種型別!同時把responses中的相應的資料取出來。因為responses={"text":xhr.responseText}是這種型別,這個方法最後形成的返回資料為responses["text"]=xhr.responseText,也就是得到伺服器端的資料!
(5)ajaxConverter作用:左後返回一個物件,該物件有state和data屬性,如{state:"success",data:response}其中response就是上面提到的經過處理的伺服器端返回的資料!
(6)如果指定了global表示支援全域性事件的呼叫,那麼在jQuery.active的值為0的時候呼叫一次ajaxStart,呼叫完成以後讓active自增,在呼叫ajaxStop之前首先讓active自減,如果是0才會呼叫ajaxStop!