針對獲取到location.href的兼容代碼:javascript
- try {
- ajaxLocation = location.href;
- } catch( e ) {
-
-
- ajaxLocation = document.createElement( "a" );
- ajaxLocation.href = "";
- ajaxLocation = ajaxLocation.href;
- }
note:若是經過location.href獲取地址出錯,那麼咱們就經過建立A標籤,而後獲取該標籤的href!在IE中能夠打印主機名,如"http://locahost:8080/"
關於去除URL中的hash值,同時兼容IE7,若是沒有協議字段咱們要手動添加:php
- var ajaxLocation="http://localhost:8080/qinl/a.action?#23"
- var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/;
- var rhash = /#.*$/;
- var rprotocol = /^\/\
- var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
- var result=ajaxLocation.replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
匹配是否跨域請求的部分:html
- var rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/;
- alert(rurl.exec("http://localhost:8080/qinl/xx.action"));
- var ajaxLocation=location.href;
- var ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
- alert(ajaxLocParts);
首先來一段精簡版的$.ajax方法:也就是其中的原理java
- var completeDeferred = jQuery.Callbacks("once memory");
- var dfd=new $.Deferred();
- var jqXHR={
- }
- dfd.promise(jqXHR).complete=completeDeferred.add;
- var f1=function()
- {
- alert("f1");
- }
- jqXHR.done(f1).complete(f1);
- completeDeferred.fire();
- dfd.resolve();
ajax源碼分析:node
- ajax: function( url, options ) {
-
-
- if ( typeof url === "object" ) {
- options = url;
- url = undefined;
- }
-
-
- options = options || {};
- var
- parts,
-
- i,
-
- cacheURL,
-
- responseHeadersString,
-
- timeoutTimer,
-
- fireGlobals,
- transport,
-
- responseHeaders,
-
- s = jQuery.ajaxSetup( {}, options ),
-
-
- callbackContext = s.context || s,
-
-
-
- globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
- jQuery( callbackContext ) :
- jQuery.event,
-
-
- deferred = jQuery.Deferred(),
-
- completeDeferred = jQuery.Callbacks("once memory"),
-
-
- statusCode = s.statusCode || {},
-
- requestHeaders = {},
- requestHeadersNames = {},
-
- state = 0,
-
- strAbort = "canceled",
-
-
-
- jqXHR = {
- readyState: 0,
-
- 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 ? null : match;
- },
-
-
-
- getAllResponseHeaders: function() {
- return state === 2 ? responseHeadersString : null;
- },
-
-
-
- setRequestHeader: function( name, value ) {
- var lname = name.toLowerCase();
-
- if ( !state ) {
- name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
- requestHeaders[ name ] = value;
- }
- return this;
- },
-
-
-
- overrideMimeType: function( type ) {
- if ( !state ) {
- s.mimeType = type;
- }
- return this;
- },
-
-
- statusCode: function( map ) {
- var code;
- if ( map ) {
- if ( state < 2 ) {
- for ( code in map ) {
-
- statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
- }
- } else {
-
- jqXHR.always( map[ jqXHR.status ] );
- }
- }
- return this;
- },
-
-
-
- abort: function( statusText ) {
- var finalText = statusText || strAbort;
- if ( transport ) {
- transport.abort( finalText );
- }
- done( 0, finalText );
- return this;
- }
- };
-
-
-
-
-
-
- deferred.promise( jqXHR ).complete = completeDeferred.add;
-
- jqXHR.success = jqXHR.done;
-
-
- jqXHR.error = jqXHR.fail;
-
-
-
-
-
- s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
-
-
-
- s.type = options.method || options.type || s.method || s.type;
-
-
-
- s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
-
-
-
- if ( s.crossDomain == null ) {
-
- parts = rurl.exec( s.url.toLowerCase() );
-
-
- 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" ) ) )
- );
- }
-
-
-
- if ( s.data && s.processData && typeof s.data !== "string" ) {
- s.data = jQuery.param( s.data, s.traditional );
- }
-
-
- inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
-
-
-
- if ( state === 2 ) {
- return jqXHR;
- }
-
-
-
-
- fireGlobals = jQuery.event && s.global;
-
- if ( fireGlobals && jQuery.active++ === 0 ) {
- jQuery.event.trigger("ajaxStart");
- }
-
-
-
- s.type = s.type.toUpperCase();
-
-
-
-
- s.hasContent = !rnoContent.test( s.type );
-
-
-
-
- cacheURL = s.url;
-
-
-
- if ( !s.hasContent ) {
-
-
- if ( s.data ) {
-
-
-
- cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
-
- delete s.data;
- }
-
-
-
- if ( s.cache === false ) {
-
-
- s.url = rts.test( cacheURL ) ?
-
-
-
- cacheURL.replace( rts, "$1_=" + nonce++ ) :
-
-
- cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
- }
- }
-
-
-
-
- if ( s.ifModified ) {
-
-
- if ( jQuery.lastModified[ cacheURL ] ) {
- jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
- }
-
-
- if ( jQuery.etag[ cacheURL ] ) {
- jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
- }
- }
-
-
-
-
- if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
- jqXHR.setRequestHeader( "Content-Type", s.contentType );
- }
-
-
-
-
- jqXHR.setRequestHeader(
- "Accept",
- s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
-
-
-
-
-
-
-
-
- s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
- s.accepts[ "*" ]
- );
-
-
- for ( i in s.headers ) {
- jqXHR.setRequestHeader( i, s.headers[ i ] );
- }
-
-
-
-
-
- if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
-
- return jqXHR.abort();
- }
-
- strAbort = "abort";
-
-
-
-
- for ( i in { success: 1, error: 1, complete: 1 } ) {
- jqXHR[ i ]( s[ i ] );
- }
-
-
-
- transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
-
- if ( !transport ) {
- done( -1, "No Transport" );
- } else {
-
-
- jqXHR.readyState = 1;
-
-
-
-
- if ( fireGlobals ) {
- globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
- }
-
-
-
- if ( s.async && s.timeout > 0 ) {
- timeoutTimer = setTimeout(function() {
- jqXHR.abort("timeout");
- }, s.timeout );
- }
-
- try {
- state = 1;
- transport.send( requestHeaders, done );
- } catch ( e ) {
-
- if ( state < 2 ) {
- done( -1, e );
-
- } else {
- throw e;
- }
- }
- }
總結:jquery
(1)調用 jQuery.ajaxSetup( {}, options )讓最終options具備jQuery內置的全部的屬性,同時也包括用戶調用ajax方法的時候傳入的全部的屬性和方法!
(2)建立jqXHR對象,讓該對象具備Deferred的全部屬性和方法,該Deferred對象能夠綁定用戶的success和error方法。可是用戶傳入的compelte方法表示任何狀況下都會調用,咱們就引入了一個Callbacks對象,把complete回調函數存入該Callback中(用fireWith調用)ajax
(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"事件promise
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;
-
-
- if ( state === 2 ) {
- return;
- }
-
-
- state = 2;
-
-
- if ( timeoutTimer ) {
- clearTimeout( timeoutTimer );
- }
-
-
- transport = undefined;
-
-
- responseHeadersString = headers || "";
-
-
- jqXHR.readyState = status > 0 ? 4 : 0;
-
-
- isSuccess = status >= 200 && status < 300 || status === 304;
-
-
- if ( responses ) {
- response = ajaxHandleResponses( s, jqXHR, responses );
- }
-
- response = ajaxConvert( s, response, jqXHR, isSuccess );
-
- if ( isSuccess ) {
-
-
- 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 ( status === 204 || s.type === "HEAD" ) {
- statusText = "nocontent";
-
-
- } else if ( status === 304 ) {
- statusText = "notmodified";
-
-
- } else {
- statusText = response.state;
- success = response.data;
- error = response.error;
- isSuccess = !error;
- }
- } else {
-
-
-
- error = statusText;
- if ( status || !statusText ) {
- statusText = "error";
- if ( status < 0 ) {
- status = 0;
- }
- }
- }
-
-
- jqXHR.status = status;
- jqXHR.statusText = ( nativeStatusText || statusText ) + "";
-
- if ( isSuccess ) {
-
-
- deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
- } else {
- deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
- }
-
- jqXHR.statusCode( statusCode );
- statusCode = undefined;
-
- if ( fireGlobals ) {
- globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
- [ jqXHR, s, isSuccess ? success : error ] );
- }
-
-
-
- completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
- if ( fireGlobals ) {
-
- globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
-
-
- 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!