什么是類型轉化器?
jQuery支持不同格式的數據返回形式,比如dataType為 xml, json,jsonp,script, or html
但是瀏覽器的XMLHttpRequest對象對數據的響應只有 responseText與responseXML 二種
所以現在我要定義dataType為jsonp,那么所得的最終數據是一個json的鍵值對,所以jQuery內部就會默認幫你完成這個轉化工作
jQuery為了處理這種執行后數據的轉化,就引入了類型轉化器,如果沒有指定類型就依據響應頭Content-Type自動處理
數據傳輸,服務器只能返回字符串形式的,所以如果我們dataType為jsop或者json的時候
服務器返回的數據為
responseText: "{"a":1,"b":2,"c":3,"d":4,"e":5}"
給轉化成
responseJSON: Object { a: 1 b: 2 c: 3 d: 4 e: 5 }
服務器的傳輸返回的只能是string類型的數據,但是用戶如果通過jQuery的dataType定義了json的格式后,會默認把數據轉換成Object的形式返回
這就是jQuery內部做的智能處理了
jQuery內把自定義的dataType與服務器返回的數據做相對應的映射處理,通過converters存儲對應的處理句柄
把需要類型轉換器ajaxConvert在服務端響應成功后,對定義在jQuery. ajaxSettings中的converters進行遍歷,找到與數據類型相匹配的轉換函數,並執行。
converters的映射:
converters: { // Convert anything to text、 // 任意內容轉換為字符串 // window.String 將會在min文件中被壓縮為 a.String "* text": window.String, // Text to html (true = no transformation) // 文本轉換為HTML(true表示不需要轉換,直接返回) "text html": true, // Evaluate text as a json expression // 文本轉換為JSON "text json": jQuery.parseJSON, // Parse text as xml // 文本轉換為XML "text xml": jQuery.parseXML }
除此之外還有額外擴展的一部分jsonp的處理
// Ajax請求設置默認的值 jQuery.ajaxSetup({ /** * 內容類型發送請求頭(Content-Type),用於通知服務器該請求需要接收何種類型的返回結果。 * 如果accepts設置需要修改,推薦在$.ajaxSetup() 方法中設置一次。 * @type {Object} */ accepts: { script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" }, contents: { script: /(?:java|ecma)script/ }, converters: { "text script": function(text) { jQuery.globalEval(text); return text; } } });
所以其格式就是
text –> (html,json,script)的處理了
其寓意就是服務器返回的用於只是string類型的文本格式,需要轉化成用戶想要的dataType類型的數據
{"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML}
類型的轉化都是發生在服務器返回數據后,所以對應的就是ajax 方法中的done之后,當然這個done方法也是經過請求分發器包裝過的,至於為什么要這樣處理上章就已經提過到了,為了處理正常請求與jsonp的跨域請求的問題
所以當AJAX請求完成后,會調用閉包函數done,在done中判斷本次請求是否成功,如果成功就調用ajaxConvert對響應的數據進行類型轉換
所以在此之前需要:
1:正確分配dataType類型,如果用戶不設置(空)的情況
2:需要轉化成converters映射表對應的格式比如(* text, text html , text xml , text json)
dataType類型的轉化
dataType類型的參數,可以是xml, json, script, or html 或者干脆為空,那么jQuery就需要一個只能的方法去判斷當前是屬於什么數據處理
因此就引入了
ajaxHandleResponses 處理響應轉化器,解析出正確的dataType類型
response = ajaxHandleResponses(s, jqXHR, responses);
dataType無法就那么幾種情況
1:dataType為空,自動轉化
此時jQuery只能根據頭部信息是猜測當前需要處理的類型
// 刪除掉通配dataType,得到返回的Content-Type while (dataTypes[0] === "*") { dataTypes.shift(); if (ct === undefined) { ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); } }
通過xhr.getAllResponseHeaders()得到頭部信息,然后去匹配Content-Type所有對象的值即可
當然找到這個Content-Type = “html”,我們還得看看有沒有對應處理的方法,如果有就需要替換這個dataTypes
// 看看是不是我們能處理的Content-Type,比如圖片這類二進制類型就不好處理了 if (ct) { // 實際上能處理的就是text、xml和json for (type in contents) { if (contents[type] && contents[type].test(ct)) { dataTypes.unshift(type); break; } } }
經過這個流程后,dataTypes 本來是* 就變成了對應的html了,這是jquery內部的自動轉化過程
2:dataType開發者指定
xml, json, script, html, jsop
總結:
類型轉換器將服務端響應的responseText或responseXML,轉換為請求時指定的數據類型dataType,
如果沒有指定類型就依據響應頭Content-Type自動處理
類型轉換器的執行過程
response = ajaxConvert(s, response, jqXHR, isSuccess);
源碼部分
function ajaxConvert(s, response, jqXHR, isSuccess) { current = dataTypes.shift(); while (current) { if (current) { // 如果碰到了*號,即一個任意類型,而轉換為任意類型*沒有意義 if (current === "*") { current = prev; // 轉化的重點 // 如果不是任意的類型,並且找到了一個不同的類型 } else if (prev !== "*" && prev !== current) { // Seek a direct converter // 組成映射格式,匹配轉化器 // * text: function String() { [native code] } // script json: function () { // text html: true // text json: function parse() { [native code] } // text script: function (text) { // text xml: function (data) { conv = converters[prev + " " + current] || converters["* " + current]; // If none found, seek a pair // 假如找不到轉化器 // jsonp是有瀏覽器執行的呢,還是要調用globalEval if (!conv) { //............... } // Apply converter (if not an equivalence) // 如果有對應的處理句柄,執行轉化 if (conv !== true) { // Unless errors are allowed to bubble, catch and return them if (conv && s["throws"]) { response = conv(response); } else { try { //執行對應的處理句柄,傳入服務器返回的數據 response = conv(response); } catch (e) { return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; } } } } } } return { state: "success", data: response }; }
流程
1.遍歷dataTypes中對應的處理規則【"script","json"】
2.制作jqXHR對象的返回數據接口
- json: "responseJSON"
- text: "responseText"
- xml: "responseXML"
如:jqXHR.responseText: "{"a":1,"b":2,"c":3,"d":4,"e":5}"
3. 生成轉化器對應的匹配規則,尋找合適的處理器
4. 返回處理后的數據response
分析一下特殊的jsonp的轉化流程
先看看轉化對應的處理器
jsonp:
converters["script json"] = function() { if (!responseContainer) { jQuery.error(callbackName + " was not called"); } return responseContainer[0]; };
jsonp的轉化器只是很簡單的從responseContainer取出了對應的值,所以responseContainer肯定在轉化之后就應該把數據給轉化成數組對象了
當然做源碼分析需要一點自己想猜想能力,比如
responseContainer這個數組對象如何而來?
那么我們知道jsonp的處理的原理,還是通過加載script,然后服務器返回一個回調函數,responseContainer數據就是回調函數的實參
所以需要滿足responseContainer的處理,必須要先滿足腳本先加載,所以我們要去分發器中找對應的加載代碼
首先responseContainer是內部變量,只有一個來源處,在預處理的時候增加一個全局的臨時函數
然后代碼肯定是執行了這個函數才能把arguments參數賦給responseContainer
overwritten = window[callbackName]; window[callbackName] = function() { responseContainer = arguments; };
callbcakName是內部創建的一個尼瑪函數名
jQuery203029543792246840894_1403062512436 = function() { responseContainer = arguments; };
我們發送請求
http://192.168.1.114/yii/demos/test.php?backfunc=jQuery203029543792246840894_1403062512436&action=aaron&_=1403062601515
服務器那邊就回調后,執行了jQuery203029543792246840894_1403062512436(responseContainer );
所以全局的callbackName函數需要在分發器中腳本加載后才能執行,從而才能截取到服務器返回的數據
我也不可能每個都分析到位,所以大家有選擇的自己根據需求去看源碼吧,大體的流程思路理解的,看起來就很快了,至於其余的類型,在之后遇到了就會在分析了