1. 程式人生 > >jquery中的$.ajax()的原始碼分析

jquery中的$.ajax()的原始碼分析

針對獲取到location.href的相容程式碼:

[javascript] view plain copy
  1. try {  
  2. ajaxLocation = location.href;  
  3. catch( e ) {  
  4.     // Use the href attribute of an A element  
  5.     // since IE will modify it given document.location  
  6.     ajaxLocation = document.createElement( "a" );  
  7.     ajaxLocation.href = "";  
  8.     ajaxLocation = ajaxLocation.href;  
  9. }  

note:如果通過location.href獲取地址出錯,那麼我們就通過建立A標籤,然後獲取該標籤的href!在IE中可以列印主機名,如"http://locahost:8080/"
關於去除URL中的hash值,同時相容IE7,如果沒有協議欄位我們要手動新增:

[javascript] view plain copy
  1. var ajaxLocation="http://localhost:8080/qinl/a.action?#23"  
  2. var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/;  
  3. var rhash = /#.*$/;  
  4. //匹配開頭的"//"欄位  
  5. var rprotocol = /^\/\//;  
  6. //獲取前面的協議欄位,如"http:","file:"等  
  7. var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];  
  8. //第一個replace去掉用hash值,第二個replace表示如果去掉hash值以後開頭已經是//那麼也要進行相應的處理  
  9. var result=ajaxLocation.replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );  

匹配是否跨域請求的部分:

[javascript] view plain copy
  1. //測試程式碼1:  
  2. var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/; //列印[http://localhost:8080,http:,localhost,8080]  
  3. alert(rurl.exec("http://localhost:8080/qinl/xx.action"));  
  4. //測試程式碼2:  
  5. //http://www.365mini.com/diy.php?f=jquery_ajax-demo  
  6. var ajaxLocation=location.href;  
  7. var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];  
  8. //列印[http://www.365mini.com,http:,www.365mini.com,]  
  9. alert(ajaxLocParts);  

首先來一段精簡版的$.ajax方法:也就是其中的原理

[javascript] view plain copy
  1. var completeDeferred = jQuery.Callbacks("once memory");  
  2. var dfd=new $.Deferred();  
  3. var jqXHR={  
  4. }  
  5. //jqXHR有了promise所有方法,但是修改狀態還是要靠Deferred物件!  
  6. //同時為這個jqXHR封裝一個Callbacks物件,呼叫complete就想相當於向其中新增  
  7. //回撥函式,然後要觸發他就直接呼叫fireWith才行!  
  8. dfd.promise(jqXHR).complete=completeDeferred.add;  
  9. var f1=function()  
  10. {  
  11.     alert("f1");  
  12. }  
  13. //為done和complete添加回調函式  
  14. jqXHR.done(f1).complete(f1);  
  15. //呼叫fire觸發所有的complete新增的回撥  
  16. completeDeferred.fire();  
  17. //觸發Deferred物件的所有的done集合中函式,記住這裡  
  18. //不能是dfd呼叫,因為他沒有resolve方法不能改變狀態!  
  19. dfd.resolve();  

ajax原始碼分析:

[javascript] view plain copy
  1. ajax: function( url, options ) {  
  2.          //ajax方法引數調整,如果url是object,這是我們一般的呼叫方式  
  3.         // If url is an object, simulate pre-1.5 signature  
  4.         if ( typeof url === "object" ) {  
  5.             options = url;  
  6.             url = undefined;  
  7.         }  
  8.               //option是一個物件  
  9.         // Force options to be an object  
  10.         options = options || {};  
  11.         var // Cross-domain detection vars  
  12.             parts,  
  13.             // Loop variable  
  14.             i,  
  15.             // URL without anti-cache param  
  16.             cacheURL,  
  17.             // Response headers as string  
  18.             responseHeadersString,  
  19.             // timeout handle  
  20.             timeoutTimer,  
  21.             // To know if global events are to be dispatched  
  22.             fireGlobals,  
  23.             transport,  
  24.             // Response headers  
  25.             responseHeaders,  
  26.             // Create the final options object  
  27.             s = jQuery.ajaxSetup( {}, options ),  
  28.             // Callbacks context  
  29.             //設定context,如果沒有context那麼就是返回的最終的options=ajaxSettings+options(使用者呼叫ajax方法時候傳送的option)  
  30.             callbackContext = s.context || s,  
  31.             //如果傳入的物件有context,同時context是DOM物件或者是jQuery物件,那麼把該DOM物件封裝為jQuery物件  
  32.             //如果不滿足也就是沒有context或者context不是DOM物件和jQuery物件,那麼globalEventContext就是jQuery.event物件!  
  33.             // Context for global events is callbackContext if it is a DOM node or jQuery collection  
  34.             globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?  
  35.                 jQuery( callbackContext ) :  
  36.                 jQuery.event,  
  37.             //建立Deferred物件  
  38.             // Deferreds  
  39.             deferred = jQuery.Deferred(),  
  40.             //建立Callbacks物件  
  41.             completeDeferred = jQuery.Callbacks("once memory"),  
  42.             //獲取最終options的statusCode引數,預設是空物件!  
  43.             // Status-dependent callbacks  
  44.             statusCode = s.statusCode || {},  
  45.             // Headers (they are sent all at once)  
  46.             requestHeaders = {},  
  47.             requestHeadersNames = {},  
  48.             // The jqXHR state  
  49.             state = 0,  
  50.             // Default abort message  
  51.             strAbort = "canceled",  
  52.             //建立一個偽的xhr物件,該物件有readyState,getResponseHeader,getAllResponseHeaders,setRequestHeader  
  53.             //overrideMimeType,statusCode,abort方法和屬性!  
  54.             // Fake xhr  
  55.             jqXHR = {  
  56.                 readyState: 0,  
  57.                 // Builds headers hashtable if needed  
  58.                 getResponseHeader: function( key ) {  
  59.                     var match;  
  60.                     //狀態是2的時候才能獲取資料  
  61.                     if ( state === 2 ) {  
  62.                         if ( !responseHeaders ) {  
  63.                             responseHeaders = {};  
  64.                             //rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL  
  65.                             //responseHeaders的鍵名就是第一個捕獲組的資料,第二個鍵值就是第二個捕獲組資料!  
  66.                             while ( (match = rheaders.exec( responseHeadersString )) ) {  
  67.                                 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];  
  68.                             }  
  69.                         }  
  70.                         //返回這個key對應的鍵值!  
  71.                         match = responseHeaders[ key.toLowerCase() ];  
  72.                     }  
  73.                     return match == null ? null : match;  
  74.                 },  
  75.                 // Raw string  
  76.                 //如果狀態是2,那麼就是responseHeadersString  
  77.                 getAllResponseHeaders: function() {  
  78.                     return state === 2 ? responseHeadersString : null;  
  79.                 },  
  80.                 // Caches the header  
  81.                 //設定HTTP請求頭的時候,頭是小寫的  
  82.                 setRequestHeader: function( name, value ) {  
  83.                     var lname = name.toLowerCase();  
  84.                     //如果state為0那麼就快取頭,把結果放入requestHeaders中!但是要提前查詢requestHeadersNames  
  85.                     if ( !state ) {  
  86.                         name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;  
  87.                         requestHeaders[ name ] = value;  
  88.                     }  
  89.                     return this;  
  90.                 },  
  91.                 // Overrides response content-type header  
  92.                 //如果state=0那麼可以覆蓋這個mimetype!  
  93.                 overrideMimeType: function( type ) {  
  94.                     if ( !state ) {  
  95.                         s.mimeType = type;  
  96.                     }  
  97.                     return this;  
  98.                 },  
  99.                 // Status-dependent callbacks  
  100.                 statusCode: function( map ) {  
  101.                     var code;  
  102.                     if ( map ) {  
  103.                         if ( state < 2 ) {  
  104.                             for ( code in map ) {  
  105.                                 // Lazy-add the new callback in a way that preserves old ones  
  106.                                 statusCode[ code ] = [ statusCode[ code ], map[ code ] ];  
  107.                             }  
  108.                         } else {  
  109.                             // Execute the appropriate callbacks  
  110.                             jqXHR.always( map[ jqXHR.status ] );  
  111.                         }  
  112.                     }  
  113.                     return this;  
  114.                 },  
  115.                 // Cancel the request  
  116.                 //取消請求  
  117.                 abort: function( statusText ) {  
  118.                     var finalText = statusText || strAbort;  
  119.                     if ( transport ) {  
  120.                         transport.abort( finalText );  
  121.                     }  
  122.                     done( 0, finalText );  
  123.                     return this;  
  124.                 }  
  125.             };  
  126.         // Attach deferreds  
  127.         //讓jqXHR具有promise的所有的屬性和方法!不包括狀態改變的方法,如resollveWith等  
  128.         //同時jqXHR的complete對應於completeDeferred的add方法,但是該jqXHR中也封裝了三個Callbacks物件  
  129.         //但是這裡沒有用內部的Callbacks物件,而是採用一個新的Callbacks物件  
  130.         //completeDeferred = jQuery.Callbacks("once memory"),  
  131.         deferred.promise( jqXHR ).complete = completeDeferred.add;  
  132.         //success呼叫的promise物件內建的done方法對應於的Callbacks物件  
  133.         jqXHR.success = jqXHR.done;  
  134.         //error方法呼叫的promise物件內建的fail方法對應的Callbacks物件!  
  135.         //注意:這個內建的promise物件的progress方法對應的Callbacks物件沒有用到!  
  136.         jqXHR.error = jqXHR.fail;  
  137.         // Remove hash character (#7531: and string promotion)  
  138.         // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)  
  139.         // Handle falsy url in the settings object (#10093: consistency with old signature)  
  140.         // We also use the url parameter if available  
  141.         s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );  
  142.         //type就是get或者post。這個方式可以通過使用者傳入的物件的method或者type或者最終的物件的method或者type獲取!  
  143.         // Alias method option to type as per ticket #12004  
  144.         s.type = options.method || options.type || s.method || s.type;  
  145.         // Extract dataTypes list  
  146.         //取出dataType兩邊的空格,同時通過空格進行分組得到一個數組!dataType="html"  
  147.         s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];  
  148.         //如果沒有crossDomain物件  
  149.         // A cross-domain request is in order when we have a protocol:host:port mismatch  
  150.         if ( s.crossDomain == null ) {  
  151.             parts = rurl.exec( s.url.toLowerCase() );  
  152.             //如果在同一個域名裡面那麼這裡的判斷都是false,結果就是crossDomain為false  
  153.             //如果不再同一個域名裡面,那麼這裡的判斷都是true,結果就是crossDomain為true!  
  154.             s.crossDomain = !!( parts &&  
  155.                 ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||  
  156.                     ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==  
  157.                         ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )  
  158.             );  
  159.         }  
  160.          //如果存在data同時存在processData同時data不是string!  
  161.          //traditional是為了相容jQuery<1.3.2行為的!  
  162.         // Convert data if not already a string  
  163.         if ( s.data && s.processData && typeof s.data !== "string" ) {  
  164.             s.data = jQuery.param( s.data, s.traditional );  
  165.         }  
  166.         // Apply prefilters  
  167.         inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );  
  168.         //如果在預過濾器中已經終止了請求,那麼直接返回jqXHR物件!  
  169.         // If request was aborted inside a prefilter, stop there  
  170.         if ( state === 2 ) {  
  171.             return jqXHR;  
  172.         }  
  173.         // We can fire global events as of now if asked to  
  174.         // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)  
  175.         //如果是global引數,那麼我們直接trigger事件ajaxStart!  
  176.         fireGlobals = jQuery.event && s.global;  
  177.         // Watch for a new set of requests  
  178.         if ( fireGlobals && jQuery.active++ === 0 ) {  
  179.             jQuery.event.trigger("ajaxStart");  
  180.         }  
  181.         // Uppercase the type  
  182.         //把type變成大寫  
  183.         s.type = s.type.toUpperCase();  
  184.         // Determine if request has content  
  185.         //rnoContent = /^(?:GET|HEAD)$/  
  186.         //也就是如果沒有指定type也就是請求方式!  
  187.         s.hasContent = !rnoContent.test( s.type );  
  188.         // Save the URL in case we're toying with the If-Modified-Since  
  189.         // and/or If-None-Match header later on  
  190.         //獲取url引數!  
  191.         cacheURL = s.url;  
  192.         // More options handling for requests with no content  
  193.         //如果指定了請求方式,如get,post等!  
  194.         if ( !s.hasContent ) {  
  195.          //沒有指定請求方式的時候有傳遞資料!  
  196.             // If data is available, append data to url  
  197.             if ( s.data ) {  
  198.                 //var rquery = (/\?/);  
  199.                 //如果url後面有問號,那麼直接把引數繫結到問號後面就可以了!否則新增問號在繫結!  
  200.                 //同時刪除資料!  
  201.                 cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );  
  202.                 // #9682: remove data so that it's not used in an eventual retry  
  203.                 delete s.data;  
  204.             }  
  205.             // Add anti-cache in url if needed  
  206.             //如果指定了cache為false表示不能進行資料快取,那麼會在url後面新增一個當前時間!  
  207.             if ( s.cache === false ) {  
  208.                 s.url = rts.test( cacheURL ) ?  
  209.                     // If there is already a '_' parameter, set its value  
  210.                     //var nonce = jQuery.now();  
  211.                     cacheURL.replace( rts, "$1_=" + nonce++ ) :  
  212.                     // Otherwise add one to the end  
  213.                     cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;  
  214.             }  
  215.         }  
  216.         // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.  
  217.         //如果添加了ifModified頭  
  218.         //var lastModified={}  
  219.         if ( s.ifModified ) {  
  220.             //如果lastModified儲存了這個cacheURL也就是這個URL有快取了!那麼直接新增頭If-Modified-Since資料為  
  221.             //jQuery.lastModified[ cacheURL ]獲取到的資料!  
  222.             if ( jQuery.lastModified[ cacheURL ] ) {  
  223.                 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );  
  224.             }  
  225.             //如果在etag: {}中儲存了這個URL  
  226.             //那麼新增If-None-Match,因為Etag和if-None-Match是一對,Last-Modified和If-Modified-Since是一對!  
  227.             if ( jQuery.etag[ cacheURL ] ) {  
  228.                 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );  
  229.             }  
  230.         }  
  231.         // Set the correct header, if data is being sent  
  232.         //如果有資料傳送,同時也指定了get,post方法,同時contentType也指定!  
  233.         //那麼新增一個頭Content-Type!  
  234.         if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {  
  235.             jqXHR.setRequestHeader( "Content-Type", s.contentType );  
  236.         }  
  237.         // Set the Accepts header for the server, depending on the dataType  
  238.         //同時新增請求頭Accept  
  239.         //(1)如果指定了dataType,同時accepts中dataType存在,也就是必須是指定的data[type]  
  240.         jqXHR.setRequestHeader(  
  241.             "Accept",  
  242.             s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?  
  243.             //  allTypes = "*/".concat("*");  
  244.             //如果支援的資料型別是內建的型別,那麼獲取內建的值,如text獲取的就是"text/plain"  
  245.             //同時dataTypes[0]不是"*",那麼我們加上一個逗號,同時加上後面剩餘的部分!  
  246.             /* 
  247.             var allTypes = "*/".concat("*");  
  248.             //列印  
  249.             //alert(allTypes);  
  250.             //最後的格式就是:text/html,*/*;q=0.01  
  251.             //如果傳入的dataType就是*,那麼最後的結果就是"*/*"  
  252.                 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :  
  253.                 s.accepts[ "*" ]  
  254.         );  
  255.         // Check for headers option  
  256.         //如果還定義了headers選項,那麼會被逐個傳送到伺服器端!  
  257.         for ( i in s.headers ) {  
  258.             jqXHR.setRequestHeader( i, s.headers[ i ] );  
  259.         }  
  260.         // Allow custom headers/mimetypes and early abort  
  261.         //如果指定了beforeSend,同時beforeSend的函式呼叫的結果是false或者state是2,那麼取消這次請求  
  262.         //beforeSend中傳入的引數為callbackContext = s.context || s也就是最終物件的context引數為上下文  
  263.         //第一個引數是XHR物件,第二個引數是最終的options物件!  
  264.         if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {  
  265.             // Abort if not done already and return  
  266.             return jqXHR.abort();  
  267.         }  
  268.         // aborting is no longer a cancellation  
  269.         strAbort = "abort";  
  270.         // Install callbacks on deferreds  
  271.         //往jqXHR["success"]和jqXHR["error"],jqXHR["complete"]中添加回調函式  
  272.         //回撥函式就是通過最終options物件獲取到的success,error,complete函式!  
  273.         for ( i in { success: 1, error: 1, complete: 1 } ) {  
  274.             jqXHR[ i ]( s[ i ] );  
  275.         }  
  276.         // Get transport  
  277.         //傳入的引數是transports物件!這個函式裡面會判斷是否傳入了transports  
  278.         transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );  
  279.         // If no transport, we auto-abort  
  280.         if ( !transport ) {  
  281.             done( -1, "No Transport" );  
  282.         } else {  
  283.             //如果有transport那麼readyState就是1,表示 (載入)已呼叫send()方法,正在傳送請求,也就是請求的傳送是在  
  284.             //inspectPrefiltersOrTransports裡面完成的!  
  285.             jqXHR.readyState = 1;  
  286.             // Send global event  
  287.             //指示是否觸發全域性Ajax事件。將該值設為false將阻止全域性事件處理函式被觸發  
  288.             //fireGlobals = jQuery.event && s.global;  
  289.             //如果是表示全域性ajax事件,那麼我們要呼叫ajaxSend方法!同時為這個方法傳入引數jqXHR和最終option!  
  290.             if ( fireGlobals ) {  
  291.                 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );  
  292.             }  
  293.             // Timeout  
  294.             //如果指定了async同時timeout>0表示指定了隔多少秒就放棄  
  295.             //一個超時呼叫,超時直接呼叫abort方法!  
  296.             if ( s.async && s.timeout > 0 ) {  
  297.                 timeoutTimer = setTimeout(function() {  
  298.                     jqXHR.abort("timeout");  
  299.                 }, s.timeout );  
  300.             }  
  301.             //如果有transport,那麼呼叫send方法!  
  302.             try {  
  303.                 state = 1;  
  304.                 transport.send( requestHeaders, done );  
  305.             } catch ( e ) {  
  306.                 // Propagate exception as error if not done  
  307.                 if ( state < 2 ) {  
  308.                     done( -1, e );  
  309.                 // Simply rethrow otherwise  
  310.                 } else {  
  311.                     throw e;  
  312.                 }  
  313.             }  
  314.         }  


總結:

(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方法的處理邏輯:

[javascript] view plain copy
  1. function done( status, nativeStatusText, responses, headers ) {  
  2.         var isSuccess, success, error, response, modified,  
  3.             statusText = nativeStatusText;  
  4.         // Called once  
  5.         //如果state是2,那麼直接返回!  
  6.         if ( state === 2 ) {  
  7.             return;  
  8.         }  
  9.         // State is "done" now  
  10.         //state設定為2表示不會再次執行了!  
  11.         state = 2;  
  12.         // Clear timeout if it exists  
  13.         //如果timeoutTimer存在,那麼直接清除!  
  14.         if ( timeoutTimer ) {  
  15.             clearTimeout( timeoutTimer );  
  16.         }  
  17.         // Dereference transport for early garbage collection  
  18.         // (no matter how long the jqXHR object will be used)  
  19.         transport = undefined;  
  20.         // Cache response headers  
  21.         //獲取response的頭部資訊,預設是空!  
  22.         responseHeadersString = headers || "";  
  23.         // Set readyState  
  24.         //如果status>0那麼把readyState設定為4!  
  25.         jqXHR.readyState = status > 0 ? 4 : 0;  
  26.         // Determine if successful  
  27.         //如果status在指定的區間內那麼表示成功!  
  28.         isSuccess = status >= 200 && status < 300 || status === 304;  
  29.         // Get response data  
  30.         //如果done方法有responses那麼對他進行處理!  
  31.         if ( responses ) {  
  32.             response = ajaxHandleResponses( s, jqXHR, responses );  
  33.         }  
  34.         // Convert no matter what (that way responseXXX fields are always set)  
  35.         response = ajaxConvert( s, response, jqXHR, isSuccess );  
  36.         // If successful, handle type chaining  
  37.         if ( isSuccess ) {  
  38.             // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.  
  39.             //如果ifModified存在,那麼就要設定If-Modified-Since和If-None-Match頭!  
  40.             if ( s.ifModified ) {  
  41.                 modified = jqXHR.getResponseHeader("Last-Modified");  
  42.                 if ( modified ) {  
  43.                     jQuery.lastModified[ cacheURL ] = modified;  
  44.                 }  
  45.                 modified = jqXHR.getResponseHeader("etag");  
  46.                 if ( modified ) {  
  47.                     jQuery.etag[ cacheURL ] = modified;  
  48.                 }  
  49.             }  
  50.             // if no content  
  51.             //204表示沒有資料,這時候頁面就不需要跳轉!還是停留在當前頁面!  
  52.             if ( status === 204 || s.type === "HEAD" ) {  
  53.                 statusText = "nocontent";  
  54.                //如果是304那麼表示沒有修改內容!  
  55.             // if not modified  
  56.             } else if ( status === 304 ) {  
  57.                 statusText = "notmodified";  
  58.                 //如果有資料,那麼我們獲取到資料!  
  59.             // If we have data, let's convert it  
  60.             } else {  
  61.                 statusText = response.state;  
  62.                 success = response.data;  
  63.                 error = response.error;  
  64.                 isSuccess = !error;  
  65.             }  
  66.         } else {  
  67.         //這裡的else表示請求失敗!我們從statusText獲取到錯誤的資訊,然後對statusText進行處理!  
  68.             // We extract error from statusText  
  69.             // then normalize statusText and status for non-aborts  
  70.             error = statusText;  
  71.             if ( status || !statusText ) {  
  72.                 statusText = "error";  
  73.                 if ( status < 0 ) {  
  74.                     status = 0;  
  75.                 }  
  76.             }  
  77.         }  
  78.         //為jqXHR設定資料  
  79.         // Set data for the fake xhr object  
  80.         jqXHR.status = status;  
  81.         jqXHR.statusText = ( nativeStatusText || statusText ) + "";  
  82.         // Success/Error  
  83.         if ( isSuccess ) {  
  84.             //如果成功了請求,那麼我們傳入的Context是callbackContext,傳入的資料是response.data  
  85.             //response.status和jqXHR物件  
  86.             deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );  
  87.         } else {  
  88.             deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );  
  89.         }  
  90.         // Status-dependent callbacks  
  91.         jqXHR.statusCode( statusCode );  
  92.         statusCode = undefined;  
  93.            //如果是全域性執行  
  94.         if ( fireGlobals ) {  
  95.             globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",  
  96.                 [ jqXHR, s, isSuccess ? success : error ] );  
  97.         }  
  98.         // Complete  
  99.         //這個物件新增的所有函式執行,表示完成,不是成功,失敗,而是complelte表示不管成功與否都是會執行的!  
  100.         //而且只會執行一次!  
  101.         completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );  
  102.         if ( fireGlobals ) {  
  103.             //globalEventContext也就是最終options的事件,觸發事件ajaxComplete!  
  104.             globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );  
  105.             // Handle the global AJAX counter  
  106.             //如果全域性的ajax計數器已經是0了,那麼就會觸發ajaxStrop事件!  
  107.             if ( !( --jQuery.active ) ) {  
  108.                 jQuery.event.trigger("ajaxStop");  
  109.             }  
  110.         }  
  111.     }  
  112.     return jqXHR;  
  113. }  
  114. );  

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!

相關推薦

jQuery選擇器原始碼分析和easyui核心分析

寫在選擇器原始碼分析之前      這裡指對1.7.2版本的原始碼分析,更高版本添加了更多程式碼。      整個jQuery的程式碼是寫在一個(function(window, undefined){})(window);這樣一個閉包裡。請思考,為什麼要這樣做?      將其寫在一個閉包函式

jQuery 事件機制原始碼分析1——jQuery事件機制整體架構

之前在網上搜索過一些內容大多都是寫的零散寫出一些方法感覺不是很系統,要不就是文不對題,後來自己debug 一番大概搞清楚了,決定寫個文章記錄下來。 需要說明的有以下幾點: 1 本文不會教你如何使用.on .off .trigger 等方法  2  基於原始碼分析基於

jQuery原始碼分析系列(36) : Ajax

什麼是型別轉化器? jQuery支援不同格式的資料返回形式,比如dataType為 xml, json,jsonp,script, or html 但是瀏覽器的XMLHttpRequest物件對資料的響應只有 responseText與responseXML 二種 所以現在我要定義dataType為js

jQuery原始碼分析系列(31) : Ajax deferred實現

AJAX的底層實現都是瀏覽器提供的,所以任何基於api上面的框架或者庫,都只是說對於功能的靈活與相容維護性做出最優的擴充套件 ajax請求的流程: 1、通過 new XMLHttpRequest 或其它的形式(指IE)生成ajax的物件xhr。   2、通過xhr.open(type, url, asy

jQuery原始碼分析系列(34) : Ajax

上一章大概講了前置過濾器和請求分發器的作用,這一章主要是具體分析每種對應的處理方式 $.ajax()呼叫不同型別的響應,被傳遞到成功處理函式之前,會經過不同種類的預處理(prefilters)。 預處理的型別取決於由更加接近預設的Content-Type響應,但可以明確使用dataType選項進行設定。如果

jQuery原始碼分析系列(35) : Ajax

ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態新增<script>標籤來呼叫伺服器提供的js指令碼 json核心就是:允許使用者傳遞一個callback引數給服務端,然後服務端返回資料時會將這個callback引數作為函式名來包裹住JSON資料,這樣客

jQuery原始碼分析系列(33) : AJAX的前置過濾器和請求分發器

jQuery1.5以後,AJAX模組提供了三個新的方法用於管理、擴充套件AJAX請求,分別是: 1.前置過濾器 jQuery. ajaxPrefilter 2.請求分發器 jQuery. ajaxTransport, 3.型別轉換器 ajaxConvert 原始碼結構: jQuery.exten

jQuery原始碼分析系列(30) : Ajax 整體結構

開頭引用一段 想起一句話:前端研究,研究個屁~ 的確如此呀。補充下聯:前端設計,設計個屁~ 前端目前最大的困境是,如 HTML 一樣,無論你承不承認,市場上並不太需要 HTML 高手 其實這裡引發一個問題:前端的價值究竟是什麼?未來應該如何發展? 我個人覺得還是一個核心價值的問題

jQuery原始碼分析系列(37) : Ajax 總結

綜合前面的分析,我們總結如下3大塊: jQuery1.5以後,AJAX模組提供了三個新的方法用於管理、擴充套件AJAX請求 前置過濾器 jQuery. ajaxPrefilter 請求分發器 jQuery. ajaxTransport 型別轉換器 ajaxConvert 為了整體性與擴充

jQuery原始碼分析之$.ajax方法

針對獲取到location.href的相容程式碼: try { ajaxLocation = location.href; } catch( e ) { // Use the href attribute of an A element // since

jquery的$.ajax()的原始碼分析

針對獲取到location.href的相容程式碼: [javascript] view plain copy try {   ajaxLocation = location.href;   } catch( e ) {       // Use the href attribute of an 

jQuery原始碼分析系列 : Ajax 整體結構

jQuery.Ajax做了那些事? 我們知道AJAX的底層實現其實是很簡單的.拋開IE不說,標準的w3c直接提供了XMLHttpRequest方法 我們主要站在設計的角度理解,如何設計出低耦合高內聚的程式碼 jQuery對Ajax的處理主要體現在對瀏覽器相容,資料

jqueryajax使用error調試錯誤的方法,實例分析Ajax的使用方法與error函數調試錯誤的技巧

類型 如果 出錯 uri 控制 XML 執行命令 等待 mime類型 代碼:$(document).ready(function() { jQuery("#clearCac").click(function() { jQu

Jquery原始碼分析-整體結構

  最近在學習Jquery的最新的原始碼,Jquery-3.3.1版本。網上有很多對jquery解析的文章。但是我還是要自己去嘗試著看一篇jquery的原始碼。本系列部落格用來記錄其中的過程,並同大家分享。本次學習Jquery原始碼是結合Jquery API來學習的。結合API來學習,首先會讓我理解Jquer

STL原始碼分析之deque有序容器

前言 前一節我們分析了deque的基本使用, 本節我們來分析一下deque的對map的操作, 即插入, 刪除等. 但是本節只分析push, pop和刪除操作, 而insert操作有點複雜還是放到下節來分析. push, pop 因為deque的是能夠雙向操作, 所以其push

jQuery 2.0.3 原始碼分析 Deferred概念

      JavaScript程式設計幾乎總是伴隨著非同步操作,傳統的非同步操作會在操作完成之後,使用回撥函式傳回結果,而回調函式中則包含了後續的工作。這也是造成非同步程式設計困難的主要原因:我們一直習慣於“線性”地編寫程式碼邏輯,但是大量非同步操作所帶來的回撥函式

jQuery 2.0.3 原始碼分析 鉤子機制

jQuery提供了一些快捷函式來對dom物件的屬性進行存取操作. 這一部分還是比較簡單的. 根據API這章主要是分解5個方法 .attr()   獲取匹配的元素集合中的第一個元素的屬性的值  或 設定每一個匹配元素的一個或多個屬性。 .prop() 獲取匹配的元素集

jQuery原始碼分析系列(38) : 佇列操作

Queue佇列,如同data資料快取與Deferred非同步模型一樣,都是jQuery庫的內部實現的基礎設施 Queue佇列是animate動畫依賴的基礎設施,整個jQuery中佇列僅供給動畫使用 Queue佇列 佇列是一種特殊的線性表,只允許在表的前端(隊頭)

jQuery原始碼分析系列 : 整體架構

query這麼多年了分析都寫爛了,老早以前就拜讀過, 不過這幾年都是做移動端,一直御用zepto, 最近抽出點時間把jquery又給掃一遍 我也不會照本宣科的翻譯原始碼,結合自己的實際經驗一起拜讀吧! github上最新是jquery-master,加入了AMD規範了,我就以官方最新2.0.3為準 整體架構

jQuery原始碼分析

宣告:本文為原創文章,如需轉載,請註明來源並保留原文連結Aaron,謝謝! 版本截止到2013.8.24 jQuery官方釋出最新的的2.0.3為準 本人在慕課網的教程(完結) jQuery原始碼分析目錄(完結)