jQuery1.5以后,AJAX模塊提供了三個新的方法用於管理、擴展AJAX請求,分別是:
2.請求分發器 jQuery. ajaxTransport,
3.類型轉換器 ajaxConvert
源碼結構:
jQuery.extend({ /** * 前置過濾器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 請求分發器 * @type {[type]} */ ajaxTransport: addToPrefiltersOrTransports(transports), ........................... });
可見這2個方法是通過私有方法addToPrefiltersOrTransports通過curry手段構造的,分別是保持了prefilters與transports的引用
來個簡單的模擬這個結構
var prefilters = 2; var addToPrefiltersOrTransports = function(prefilters) { return function(b) { return prefilters + b; } } var ajaxPrefilter = addToPrefiltersOrTransports(prefilters) ajaxPrefilter(1) //3
可見ajaxPrefilter就維持了addToPrefiltersOrTransports返回函數的引用了,這種就是閉包的手法了,這也是JS的開發人員都需要掌握的
好處就是合並多個參數,當然因為維持引用代價就是一點點性能消耗
當然jQuery不是傳遞的簡單類型處理,還可以傳遞的一個引用類型的回調函數,所以針對ajaxPrefilter方法放閉包構件就需要做一些處理了
填充prefilters處理器
var prefilters = {}; var addToPrefiltersOrTransports = function(structure) { return function(func) { structure['*'] = func; } } var ajaxPrefilter = addToPrefiltersOrTransports(prefilters) ajaxPrefilter(function(options){ return { send:function(){ }, callback:function(){ } } })
其實說白了就是把對應的方法制作能函數的形式填充到prefilters或者transports對應的處理包裝對象中
要用的時候直接執行,每個函數都保持着各自的引用
這種寫法的好處自然是靈活,易維護,減少代碼量
還有我們經常的使用的,jQuery的代碼很簡練,比如合並多個方法的創建等等
jQuery.each([ "tabIndex", "readOnly", "maxLength", "contentEditable" ], function() { jQuery.propFix[this.toLowerCase()] = this; });
所以此時的prefilters中的結構就是
prefilters = { '*': function() { return { send: function() { }, callback: function() { } } } }
回歸重點,那么引入ajaxPrefilter與ajaxTransport的作用是干嘛?
前置過濾器和請求分發器在執行時,分別遍歷內部變量prefilters和transports,這兩個變量在jQuery加載完畢后立即初始化,從過閉包的方法填充這個2個對象
ajaxPrefilter與ajaxTransport都是通過inspectPrefiltersOrTransports構建器
prefilters中的前置過濾器在請求發送之前、設置請求參數的過程中被調用,調用prefilters的是函數inspectPrefiltersOrTransports;
巧妙的是,transports中的請求分發器在大部分參數設置完成后,也通過函數inspectPrefiltersOrTransports取到與請求類型匹配的請求分發器:
function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) { var inspected = {}, seekingTransport = (structure === transports); function inspect(dataType) { var selected; inspected[dataType] = true; jQuery.each(structure[dataType] || [], function(_, prefilterOrFactory) { var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR); if (typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[dataTypeOrTransport]) { options.dataTypes.unshift(dataTypeOrTransport); inspect(dataTypeOrTransport); return false; } else if (seekingTransport) { return !(selected = dataTypeOrTransport); } }); return selected; } return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*"); }
遍歷structure[dataType]數組,並執行回調
prefilterOrFactory為函數數組元素
執行該函數如果返回的結果dataTypeOrTransport是字符串且時prefilters且沒有被inspected過
就給options.dataTypes數組頭部添加該字符串
繼續遞歸dataTypeOrTransport(當我們使用json/jsonp的時候會返回“script”,於是會執行“script”相關的回調)
如果是transport就返回dataTypeOrTransport的假結果
前置過濾器 prefilters
簡單的說就是一種hack的做法,只是說比起事件的那種hack寫的手法實現更為高明
我們可以看看針對prefilters的方法其實就是dataType為 script,json,jsonp的處理
當我們動態加載腳本文件比如
$.ajax({ type : "GET", url : "test.js", dataType : "script" });
所以在inspectPrefiltersOrTransports方法中prefilters[script]能找到對應的處理方法,所以就會執行
例如script的hack,要強制加上處理緩存的特殊情況和crossDomain
因為設置script的前置過濾器,script並不一定意思着跨域
跨域未被禁用,強制類型為GET,不觸發全局時間
jQuery.ajaxPrefilter("script", function(s) { if (s.cache === undefined) { s.cache = false; } if (s.crossDomain) { s.type = "GET"; } });
所以prefilters就是在特定的環境針對特定的情況做一些必要的兼容的處理
請求分發器 transports
請求分發器顧名思義發送請求,那么底層的ajax發送請求是通過send方法
xhr.send();
但是jQuery對send方法做了拆分,把對應的處理放到了transports中了
那么transports對象也是類似前置處理器通過jQuery.ajaxTransport構建
例如script,send,abort方法
返回出transports方法
transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);