摘要:上文我們已經(jīng)介紹了的幾個(gè)副函數(shù)和,本文主要介紹主函數(shù)的內(nèi)部實(shí)現(xiàn)我們一般調(diào)用有三種寫(xiě)法第一種寫(xiě)法第二種寫(xiě)法第三種寫(xiě)法,也就是的寫(xiě)法第一種和第二種僅僅是的位置不同,內(nèi)部會(huì)判斷傳入的第一個(gè)參數(shù)是否是對(duì)象來(lái)進(jìn)行判斷使用的寫(xiě)法,需要轉(zhuǎn)換成的寫(xiě)法內(nèi)部
上文我們已經(jīng)介紹了ajax 的幾個(gè)副函數(shù)ajaxPrefilter 和ajaxTransport ,本文主要介紹ajax 主函數(shù)的內(nèi)部實(shí)現(xiàn)
我們一般調(diào)用ajax 有三種寫(xiě)法
// 第一種寫(xiě)法 $.ajax({ url:url, data:{...}, ... success:function(){}, error:function(){} }) // 第二種寫(xiě)法 $.ajax(url, { data:{...}, ... success:function(){}, error:function(){} }) // 第三種寫(xiě)法,也就是deferred的寫(xiě)法 $.ajax(url, { data:{...}, ... }).done().fail();
第一種和第二種僅僅是url 的位置不同,ajax 內(nèi)部會(huì)判斷傳入ajax 的第一個(gè)參數(shù)是否是對(duì)象來(lái)進(jìn)行判斷
// If url is an object, simulate pre-1.5 signature // 使用 $.ajax({url:url,data:data}) 的寫(xiě)法,需要轉(zhuǎn)換成 $.ajax(url, {data:data}); 的寫(xiě)法 if ( typeof url === "object" ) { options = url; url = undefined; }
ajax 內(nèi)部通過(guò)新增jqXHR 對(duì)象來(lái)增加ajax 的功能,例如statusCode 根據(jù)ajax 中設(shè)置相應(yīng)的http 狀態(tài)碼對(duì)象的函數(shù)來(lái)實(shí)現(xiàn)當(dāng)響應(yīng)的狀態(tài)碼對(duì)應(yīng)到設(shè)置的狀態(tài)碼時(shí),觸發(fā)相應(yīng)的函數(shù)
// Fake xhr // 模擬出來(lái)的 ajax ,增加原生ajax的功能 jqXHR = { readyState: 0, // Builds headers hashtable if needed getResponseHeader: function( key ) {}, // Raw string getAllResponseHeaders: function() {}, // Caches the header setRequestHeader: function( name, value ) { }, // Overrides response content-type header overrideMimeType: function( type ) {}, // Status-dependent callbacks // 狀態(tài)碼對(duì)應(yīng)后,如何觸發(fā)相關(guān)的函數(shù) statusCode: function( map ) {}, // Cancel the request // ajax時(shí)間超過(guò)timeout響應(yīng)的時(shí)間時(shí),就觸發(fā) abort 函數(shù) abort: function( statusText ) {} };
而ajax 的第三種寫(xiě)法,就是通過(guò)將jqXHR 添加到deferred 中,所以deferred 的所有方法,ajax 也可以同樣使用
// Deferreds // ajax 可以使用兩種方式來(lái)寫(xiě),鏈?zhǔn)秸{(diào)用的話,就需要使用 deferreds ,這樣就能夠觸發(fā)是否是 done還是其他狀態(tài)來(lái)觸發(fā)相應(yīng)的事件 deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks("once memory"), // Attach deferreds // 把deferred的方法,添加到 模擬的 jqXHR 中 deferred.promise( jqXHR ).complete = completeDeferred.add; jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail;
上一文中介紹了ajax 的addPrefilters 函數(shù),在ajax 發(fā)送數(shù)據(jù)之前,會(huì)通過(guò)inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); 調(diào)用所有的預(yù)處理函數(shù),發(fā)送數(shù)據(jù)時(shí),通過(guò)transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); 來(lái)發(fā)送數(shù)據(jù),transports 內(nèi)會(huì)根據(jù)是否跨域選擇哪個(gè)send/abort 函數(shù),返回的結(jié)果,通過(guò)done() 函數(shù)進(jìn)行處理
// Main method ajax: function( url, options ) { // If url is an object, simulate pre-1.5 signature // 使用 $.ajax({url:url,data:data}) 的寫(xiě)法,需要轉(zhuǎn)換成 $.ajax(url, {data:data}); 的寫(xiě)法 if ( typeof url === "object" ) { options = url; url = undefined; } // Force options to be an object options = options || {}; var transport, // URL without anti-cache param cacheURL, // Response headers responseHeadersString, responseHeaders, // timeout handle timeoutTimer, // Cross-domain detection vars parts, // To know if global events are to be dispatched fireGlobals, // Loop variable i, // Create the final options object s = jQuery.ajaxSetup( {}, options ), // 通過(guò)把options的參數(shù)放到一個(gè)新對(duì)象中,這樣就所有的參數(shù)都只會(huì)影響當(dāng)前的ajax // Callbacks context callbackContext = s.context || s, // 執(zhí)行的上下文,根據(jù)命名,應(yīng)該是回調(diào)函數(shù)的上下文 // Context for global events is callbackContext if it is a DOM node or jQuery collection // 這里應(yīng)該是觸發(fā) ajax 的全局事件,如果有設(shè)置上下文context,那么就能夠使用 jQuery( callbackContext ) ,也就是某個(gè)元素來(lái)綁定 // 否則,就使用默認(rèn)的 jQuery底層的event來(lái)調(diào)用 globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? jQuery( callbackContext ) : jQuery.event, // Deferreds // ajax 可以使用兩種方式來(lái)寫(xiě),鏈?zhǔn)秸{(diào)用的話,就需要使用 deferreds ,這樣就能夠觸發(fā)是否是 done還是其他狀態(tài)來(lái)觸發(fā)相應(yīng)的事件 deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks("once memory"), // Status-dependent callbacks // 狀態(tài)碼對(duì)應(yīng)的回調(diào)操作,和success 事件同級(jí)別 statusCode: {404:function(){alert("頁(yè)面不存在")} statusCode = s.statusCode || {}, // Headers (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, // The jqXHR state state = 0, // Default abort message strAbort = "canceled", // Fake xhr // 模擬出來(lái)的 ajax ,原生的還不夠強(qiáng)大 jqXHR = { readyState: 0, // Builds headers hashtable if needed 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; }, // Raw string getAllResponseHeaders: function() { return state === 2 ? responseHeadersString : null; }, // Caches the header setRequestHeader: function( name, value ) { var lname = name.toLowerCase(); if ( !state ) { name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; requestHeaders[ name ] = value; } return this; }, // Overrides response content-type header overrideMimeType: function( type ) { if ( !state ) { s.mimeType = type; } return this; }, // Status-dependent callbacks // 狀態(tài)碼對(duì)應(yīng)后,如何觸發(fā)相關(guān)的函數(shù) 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 // ajax時(shí)間超過(guò)timeout響應(yīng)的時(shí)間時(shí),就觸發(fā) abort 函數(shù) abort: function( statusText ) { var finalText = statusText || strAbort; if ( transport ) { transport.abort( finalText ); } done( 0, finalText ); return this; } }; // Attach deferreds // 把deferred的方法,添加到 模擬的 jqXHR 中 deferred.promise( jqXHR ).complete = completeDeferred.add; jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail; // Remove hash character (#7531: and string promotion) // Add protocol if not provided (prefilters might expect it) // Handle falsy url in the settings object (#10093: consistency with old signature) // We also use the url parameter if available // 把地址中 hash 設(shè)置為空,因?yàn)樵赼jax中,hash值是沒(méi)有用處的,如果地址中有//,就替換成 http:// 這樣的標(biāo)準(zhǔn)寫(xiě)法 s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ) .replace( rprotocol, ajaxLocParts[ 1 ] + "http://" ); // 如果地址寫(xiě)成 //data.php 就會(huì)轉(zhuǎn)換成 http://data.php // Alias method option to type as per ticket #12004 // get/post 也可以使用 method 或者是 type s.type = options.method || options.type || s.method || s.type; // Extract dataTypes list // text html json 如果這樣寫(xiě) dataType,就需要轉(zhuǎn)換成一個(gè)集合 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""]; // A cross-domain request is in order when we have a protocol:host:port mismatch // 檢測(cè)是否跨域 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" ) ) ) ); } // Convert data if not already a string // 處理使用 ajax 傳遞的數(shù)據(jù) if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); } // Apply prefilters, // 這時(shí),觸發(fā)所有的預(yù)處理函數(shù) // s 就是 ajax 所有的參數(shù), options 開(kāi)發(fā)者傳進(jìn)來(lái)的參數(shù) inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // If request was aborted inside a prefilter, stop there // 如果在 prefilter 里面的請(qǐng)求就被終止了,就放回當(dāng)前對(duì)象 if ( state === 2 ) { return jqXHR; } // We can fire global events as of now if asked to fireGlobals = s.global; // Watch for a new set of requests // 全局事件觸發(fā),這時(shí)候沒(méi)有使用具體的元素,直接使用的默認(rèn)是 document 觸發(fā) $(document).on("ajaxStart",function(){}),而不是$("div").on("ajaxStart",function(){}) if ( fireGlobals && jQuery.active++ === 0 ) { jQuery.event.trigger("ajaxStart"); } // Uppercase the type s.type = s.type.toUpperCase(); // Determine if request has content // rnoContent 為 get/head ,就是使用 ajax 的時(shí)候,把數(shù)據(jù)加到網(wǎng)址的后面 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 cacheURL = s.url; // More options handling for requests with no content if ( !s.hasContent ) { // If data is available, append data to url if ( s.data ) { cacheURL = ( s.url += ( ajax_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 // 如果不需要緩存,就會(huì)在url的后面加上時(shí)間戳,這樣每次請(qǐng)求都是一次新的請(qǐng)求 if ( s.cache === false ) { s.url = rts.test( cacheURL ) ? // If there is already a "_" parameter, set its value cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) : // Otherwise add one to the end cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++; } } // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. // 如果數(shù)據(jù)沒(méi)有變,就使用緩存的數(shù)據(jù) 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 ] ); } } // Set the correct header, if data is being sent 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 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[ "*" ] ); // Check for headers option for ( i in s.headers ) { jqXHR.setRequestHeader( i, s.headers[ i ] ); } // Allow custom headers/mimetypes and early abort // 如果停止 ajax 事件,就直接調(diào)用 abort 事件 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 // 把 success 這些事件映射到 jqXHR 中 for ( i in { success: 1, error: 1, complete: 1 } ) { jqXHR[ i ]( s[ i ] ); } // Get transport // 觸發(fā)回調(diào),ajax對(duì)象的send/abort方法,如果跨域,就在url內(nèi)創(chuàng)建script的方法,如果不跨域,就使用通過(guò)原生ajax封裝好的send/abort方法 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); // If no transport, we auto-abort if ( !transport ) { done( -1, "No Transport" ); } else { jqXHR.readyState = 1; // Send global event if ( fireGlobals ) { globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); } // Timeout // 如果請(qǐng)求的時(shí)間大于設(shè)置的timeout,就調(diào)用 abort 函數(shù) if ( s.async && s.timeout > 0 ) { timeoutTimer = setTimeout(function() { jqXHR.abort("timeout"); }, s.timeout ); } try { state = 1; // 調(diào)用transport封裝好的 send 發(fā)送 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; } } } // Callback for when everything is done function done( status, nativeStatusText, responses, headers ) { var isSuccess, success, error, response, modified, statusText = nativeStatusText; // Called once if ( state === 2 ) { return; } // State is "done" now state = 2; // Clear timeout if it exists 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 responseHeadersString = headers || ""; // Set readyState jqXHR.readyState = status > 0 ? 4 : 0; // Determine if successful isSuccess = status >= 200 && status < 300 || status === 304; // Get response data // 然后jquery進(jìn)行處理,得到合適的輸出,可以通過(guò)使用 console.log來(lái)打印出 ajax 返回的數(shù)據(jù),來(lái)看出是怎么處理的數(shù)據(jù) // console.log(responses) if ( responses ) { response = ajaxHandleResponses( s, jqXHR, responses ); } // console.log(responses) // Convert no matter what (that way responseXXX fields are always set) response = ajaxConvert( s, response, jqXHR, isSuccess ); // console.log(responses) // If successful, handle type chaining if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. // 是否需要使用緩存中的數(shù)據(jù) 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 if ( status === 204 || s.type === "HEAD" ) { statusText = "nocontent"; // 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 { // 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; } } } // Set data for the fake xhr object jqXHR.status = status; jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error // 通過(guò)返回的數(shù)據(jù)是否成功,來(lái)觸發(fā)deferred 的resolveWith和rejectWith函數(shù) if ( isSuccess ) { deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); } else { deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); } // Status-dependent callbacks // 調(diào)用ajax返回的狀態(tài)碼,來(lái)觸發(fā)開(kāi)發(fā)者自己設(shè)置的http狀態(tài)碼的函數(shù), jqXHR.statusCode( statusCode ); statusCode = undefined; // 如果設(shè)置了全局函數(shù),根據(jù)ajax是否調(diào)用成功,來(lái)選擇觸發(fā)ajaxSuccess函數(shù)還是ajaxError函數(shù) if ( fireGlobals ) { globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", [ jqXHR, s, isSuccess ? success : error ] ); } // Complete completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); // ajax 調(diào)用完成,觸發(fā)全局函數(shù) ajaxComplete,如果當(dāng)前頁(yè)全部的ajax都調(diào)用完成,就觸發(fā)全局函數(shù)ajaxStop if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); // Handle the global AJAX counter if ( !( --jQuery.active ) ) { jQuery.event.trigger("ajaxStop"); } } } // 整個(gè) ajax 調(diào)用完后,返回的是 jqXHR 對(duì)象,就可以使用deferred的一系列方法 done,fail 等方法了 return jqXHR; },
待續(xù)...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94685.html
摘要:調(diào)用的情況下,我們通常用來(lái)請(qǐng)求數(shù)據(jù)的方法有前五種方法,在的實(shí)現(xiàn)中,本質(zhì)上還是在調(diào)用第六種方法實(shí)現(xiàn)的單純?cè)谠创a中看前五個(gè)函數(shù),代碼量都很少,多一點(diǎn)也就是函數(shù),涉及到了的寫(xiě)法,在調(diào)用成功時(shí),對(duì)返回的數(shù)據(jù)使用內(nèi)部方法進(jìn)行渲 調(diào)用jQuery 的情況下,我們通常用來(lái)請(qǐng)求數(shù)據(jù)的方法有 $(element).load(url, callback) $.get(url, data, callbac...
摘要:通常的做法是,為它們指定回調(diào)函數(shù)。請(qǐng)求返回請(qǐng)求返回請(qǐng)求返回異步隊(duì)列解耦異步任務(wù)和回調(diào)函數(shù)為模塊隊(duì)列模塊事件提供基礎(chǔ)功能。 前言 jQuery整體框架甚是復(fù)雜,也不易讀懂,這幾日一直在研究這個(gè)笨重而強(qiáng)大的框架。jQuery的總體架構(gòu)可以分為:入口模塊、底層模塊和功能模塊。這里,我們以jquery-1.7.1為例進(jìn)行分析。 jquery的總體架構(gòu) 16 (function( window,...
摘要:?jiǎn)尉€程就意味著,所有任務(wù)需要排隊(duì),前一個(gè)任務(wù)結(jié)束,才會(huì)執(zhí)行后一個(gè)任務(wù)。這決定了它只能是單線程,否則會(huì)帶來(lái)很復(fù)雜的同步問(wèn)題。小結(jié)本身是單線程的,并沒(méi)有異步的特性。當(dāng)異步函數(shù)執(zhí)行時(shí),回調(diào)函數(shù)會(huì)被壓入這個(gè)隊(duì)列。 走在前端的大道上 本篇將自己讀過(guò)的相關(guān) js異步 的文章中,對(duì)自己有啟發(fā)的章節(jié)片段總結(jié)在這(會(huì)對(duì)原文進(jìn)行刪改),會(huì)不斷豐富提煉總結(jié)更新。 概念 JS 是單線程的語(yǔ)言。 單線程就意味著...
摘要:今天同學(xué)去面試,做了兩道面試題全部做錯(cuò)了,發(fā)過(guò)來(lái)給道典型的面試題前端掘金在界中,開(kāi)發(fā)人員的需求量一直居高不下。 排序算法 -- JavaScript 標(biāo)準(zhǔn)參考教程(alpha) - 前端 - 掘金來(lái)自《JavaScript 標(biāo)準(zhǔn)參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡(jiǎn)介 算法實(shí)現(xiàn) 選擇排序 簡(jiǎn)介 算法實(shí)現(xiàn) ... 圖例詳解那道 setTimeout 與循環(huán)閉包的經(jīng)典面...
摘要:選擇的理由是一個(gè)用于現(xiàn)代瀏覽器的與大體兼容的庫(kù)。環(huán)境搭建分析環(huán)境的搭建僅需要一個(gè)常規(guī)頁(yè)面和原始代碼一個(gè)常規(guī)頁(yè)面打開(kāi)的首頁(yè)即可,在開(kāi)發(fā)人員工具中即可使用原始代碼本篇分析的代碼參照,進(jìn)入該代碼分支中即可。 選擇 Zepto 的理由 Zepto is a minimalist JavaScript library for modern browsers with a largely jQue...
閱讀 1166·2023-04-25 17:28
閱讀 3617·2021-10-14 09:43
閱讀 3978·2021-10-09 10:02
閱讀 1951·2019-08-30 14:04
閱讀 3142·2019-08-30 13:09
閱讀 3281·2019-08-30 12:53
閱讀 2907·2019-08-29 17:11
閱讀 1833·2019-08-29 16:58