1. 程式人生 > >jQuery原始碼分析之$.ajax方法

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事件。

(6)如果指定了get/head請求,那麼如果有資料那麼把資料繫結到URL後面(同時儲存這個URL以便快取URL)。同時如果是指定了get/head時候還明確指定了不能快取資料,那麼我們把快取的URL後面新增一個隨機數,隨機數是當前時間!(一開始設定了快取URL是使用者傳入的ur,get/head請求等都會對URL修改)

(7)如果使用者指定了ifModified,表示只有資料改變時候才傳送請求。如果這個URL已經訪問過了,那麼我們取出訪問該URL時候伺服器給的etag和if-none-match標籤,並且把他們通過"If-None-Match和If-Modified-Since形式傳送給伺服器端,讓伺服器端去判斷資料有沒有改變。這兩個頭是在done方法中,也就是成功回撥時候設定的!

ifModifiedBoolean型別

預設值:false允許當前請求僅在伺服器資料改變時獲取新資料(如未更改,瀏覽器從快取中獲取資料)。它使用HTTP頭資訊Last-Modified來判斷。從jQuery 1.4開始,他也會檢查伺服器指定的'etag'來確定資料是否已被修改。

(8)設定資料型別content-type,把content-type的頭新增到jqXHR物件上

contentTypeString型別

預設值:'application/x-www-form-urlencoded; charset=UTF-8'。使用指定的內容編碼型別將資料傳送給伺服器。W3C的XMLHttpRequest規範規定charset始終是UTF-8,你如果將其改為其他字符集,也無法強制瀏覽器改字元編碼。

(9)設定accept頭,告訴伺服器瀏覽器能夠接受的資料型別

acceptsObject型別

預設值:取決於dataType屬性。傳送的內容型別請求頭,用於告訴伺服器——瀏覽器可以接收伺服器返回何種型別的響應。如果傳入的是"*"結果就是"*/*",否則就是如格式"text/html,*/*;q=0.01"

(10)設定使用者通過headers傳入的HTTP頭

headersObject型別1.5 新增

預設值:{}以物件形式指定附加的請求頭資訊。請求頭X-Requested-With: XMLHttpRequest將始終被新增,當然你也可以在此處修改預設的XMLHttpRequest值。headers中的值可以覆蓋beforeSend回撥函式中設定的請求頭(意即beforeSend先被呼叫)。

(11)呼叫beforeSend

beforeSendFunction型別

指定在請求傳送前需要執行的回撥函式。該函式還有兩個引數:其一是jqXHR物件,其二是當前settings物件。這是一個Ajax事件,如果該函式返回false,將取消本次ajax請求。

(12)這一步才把我們傳入的success,error,compelete放入相應的Deferred物件和Callback物件裡面,以備回撥!

(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!