調用jQuery
的狀況下,咱們一般用來請求數據的方法有javascript
$(element).load(url, callback)
$.get(url, data, callback, type)
$.post(url, data, callback, type)
$.getJSON(url, data, callback)
$.getScript(url, callback)
$.ajax(options)
前五種方法,在jQuery
的實現中,本質上仍是在調用第六種方法實現的html
$().load() -> jQuery.fn.load -> $.ajax() $.get() -> jQuery.each( [ "get", "post" ], function() {}) -> $.ajax() $.post() -> jQuery.each( [ "get", "post" ], function() {}) -> $.ajax() $.getJSON -> getJSON() -> $.get() -> $.ajax() $.getScript -> getScript() -> $.get() -> $.ajax()
單純在源碼中看前五個函數,代碼量都不多,多一點也就是$().load()
函數,涉及到了deferred
的寫法,在調用成功時,對返回的數據使用內部方法html()
進行渲染,可是代碼也不難看懂java
下面重點介紹$().ajax()
方法,ajax
內部結構爲ajax
jQuery.extend({ active // 調用ajax的次數 lastModified // 緩存標識 etag // 緩存標識 ajaxSettings // 默認參數設置 ajaxSetup // 開發者自定義參數與默認參數複製到當前ajax的參數中 ajaxPrefilter // 觸發ajax調用前的預處理函數 ajaxTransport // 觸發ajax調用後的處理函數 ajax // ajax的主函數 getJSON // getJSON 函數 getScript // getScript 函數 })
重點須要介紹的函數有三個ajaxPrefilter
,ajaxTransport
, ajax
json
ajaxPrefilter
函數經過addToPrefiltersOrTransports( prefilters )
實現的,看下jQuery
經過ajaxPrefilter
作了哪些操做跨域
jQuery.ajaxPrefilter( "script", function( s ) {})
若是dataType
是script
的話,設置緩存操做,若是是跨域的話,type
就必須設置爲get
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {})
若是dataType
爲json jsonp
的話,就須要作進一步的操做了瀏覽器
// Detect, normalize options and install callbacks for jsonp requests jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { var callbackName, overwritten, responseContainer, // 判斷json是在url中仍是在form中 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? "url" : typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" ); // Handle iff the expected data type is "jsonp" or we have a parameter to set if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { // Get callback name, remembering preexisting value associated with it // 若是jsonp的callback是個函數的話,就執行完函數後返回結果,若是不是函數,就返回自身 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback; // Insert callback into url or form data // 把callback插入到form表單中,若是使用jsonp的話,就把callback放到url的後面 if ( jsonProp ) { s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); } else if ( s.jsonp !== false ) { s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; } // Use data converter to retrieve json after script execution // responseContainer爲衝送到url的數據 s.converters["script json"] = function() { if ( !responseContainer ) { jQuery.error( callbackName + " was not called" ); } // 返回 jsonp 的callback的參數 return responseContainer[ 0 ]; }; // force json dataType s.dataTypes[ 0 ] = "json"; // Install callback // responseContainer 拿到 callback 函數中的參數,就是觸發 callback 函數的時,傳遞給 callback 的參數 // ajax 觸發成功後,success中的第一個參數就是 傳遞給 callback 的參數 overwritten = window[ callbackName ]; window[ callbackName ] = function() { responseContainer = arguments; }; // Clean-up function (fires after converters) jqXHR.always(function() { // Restore preexisting value window[ callbackName ] = overwritten; // Save back as free if ( s[ callbackName ] ) { // make sure that re-using the options doesn't screw things around s.jsonpCallback = originalSettings.jsonpCallback; // save the callback name for future use oldCallbacks.push( callbackName ); } // Call if it was a function and we have a response // 觸發 callback 函數 if ( responseContainer && jQuery.isFunction( overwritten ) ) { overwritten( responseContainer[ 0 ] ); } // 清空 callback 函數 responseContainer = overwritten = undefined; }); // Delegate to script // 注意,這裏返回script,代表會使用script的方法處理 return "script"; } });
而後再看addToPrefiltersOrTransports()
函數,會根據每種dataType
存儲對應的函數緩存
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport // 添加 prefilters(預處理器)/transports(分發處理器) 裏面的函數 function addToPrefiltersOrTransports( structure ) { // dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { // 這是在操做原生的 Ajax 的時候,沒有傳入 dataTypeExpression if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; if ( jQuery.isFunction( func ) ) { // For each dataType in the dataTypeExpression while ( (dataType = dataTypes[i++]) ) { // Prepend if requested // 有+,說明有多個回調,就把回調往前加 if ( dataType[0] === "+" ) { dataType = dataType.slice( 1 ) || "*"; (structure[ dataType ] = structure[ dataType ] || []).unshift( func ); // Otherwise append // 沒有多個回調,就日後添加 } else { (structure[ dataType ] = structure[ dataType ] || []).push( func ); } } } }; }
而後咱們再看ajaxTransport
函數,該函數也是經過addToPrefiltersOrTransports()
函數來存儲某種dataType
類型對應的函數app
// Bind script tag hack transport // 若是有跨域的話,就會有返回值,沒有跨域的話,就沒有返回值 // 這時候到 inpectPrefiltersOrTransports 函數中看 return !(selected = dataTypeOrTransport) 中的 dataTypeOrTransport 是有值的 jQuery.ajaxTransport( "script", function( s ) { // This transport only deals with cross domain requests // 若是須要跨域的狀況下 if ( s.crossDomain ) { var script, callback; return { // send函數爲在請求的url中建立script標籤 send: function( _, complete ) { // 跨域的操做,就是動態建立script script = jQuery("<script>").prop({ async: true, charset: s.scriptCharset, src: s.url }).on( // 若是加載錯誤的狀況下,就把建立的script移除,並返回結果 "load error", callback = function( evt ) { script.remove(); callback = null; if ( evt ) { complete( evt.type === "error" ? 404 : 200, evt.type ); } } ); document.head.appendChild( script[ 0 ] ); }, // 若是中途撤銷 ajax,若是有callback就執行callback函數 abort: function() { if ( callback ) { callback(); } } }; } });
總共有兩處使用ajaxTransport
函數,第二處沒有dataType
, 咱們在addPrefiltersOrTransports()
中能看到,若是隻傳遞一個參數,那麼dataType
就爲*
,這時候處理的就是,調用原生ajax
來發送數據cors
jQuery.ajaxTransport(function( options ) { var callback; // Cross domain only allowed if supported through XMLHttpRequest // 不跨域的時候,就是調原生的 ajax if ( jQuery.support.cors || xhrSupported && !options.crossDomain ) { return { send: function( headers, complete ) { var i, id, xhr = options.xhr(); xhr.open( options.type, options.url, options.async, options.username, options.password ); // 省略了部分代碼 // Callback callback = function( type ) { return function() { // 省略了部分代碼 }; // Listen to events xhr.onload = callback(); // 沒有采用 onreadystatechange(傳統是採用這種方式),全部高級的瀏覽器都支持 onload // 省略了部分代碼 // Do send the request // This may raise an exception which is actually // handled in jQuery.ajax (so no try/catch here) xhr.send( options.hasContent && options.data || null ); }, abort: function() { if ( callback ) { callback(); } } }; } });