在學習zepto的源碼的時候,不得不稱讚這些人的厲害,我雖然能看明白,可是要我寫,估計吭哧吭哧寫不出來。雖然如今不多人使用zepto了,可是學習這些源碼我相信每次看都能給我們不一樣的感覺。Deferred對象待會講,deferred我認爲是zepto設計最巧妙的地方。先來看zepto的ajax模塊。html
;(function($){ var jsonpID = +new Date(); function triggerAndReturn(context, eventName, data) { var event = $.Event(eventName); $(context).trigger(event, data); return !event.isDefaultPrevented(); } })
這裏爲何文件開頭都要使用';'這是由於在對多個js文件進行打包的時候,若是使用換行分隔代碼,當合並壓縮多個文件以後,換行符會被刪掉,連在一塊兒可能出錯,加上分號就保險了。
jsonpID在跨域jsonp的時候會使用到,這是爲了禁止使用cache。triggerAndReturn()的目的在於建立一個Event事件,而後在context觸發他,若是默認行爲被取消了,則返回false。ajax
$.ajaxSettings = { type: 'GET', success: empty, xhr: function () { return new window.XMLHttpRequest() }, cache: true, crossDomain: false }
這是ajax的初始化,默認是GET請求,xhr是新建的XMLHttpRequest()對象,cache表示瀏覽器是否應該被容許緩存GET響應。crossDomain表示是否能夠來自另一個域。
下面來看看最核心的$.ajax方法。正則表達式
$.ajax = function (options) { var settings = $.extend({}, options || {}), deferred = $.Deferred && $.Deferred(), urlAnchor, hashIndex for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] ajaxStart(settings) ... }
首先傳入options,而後將傳入的options存儲到本地,deferred我們暫時能夠把它當作一個promise對象,遍歷$.ajaxSettings,若是用戶沒有設置裏邊有的屬性,那就使用默認的屬性。而後調用ajaxStart開始的函數。json
if (!settings.crossDomain) { urlAnchor = document.createElement('a'); urlAnchor.href = settings.url; urlAnchor.href = urlAnchor.href; settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host) }
這裏首先crossDomain爲false的狀況下,進入該邏輯,經過判斷咱們傳入的url和當前window.location.href作對比,判斷是否是跨域。跨域
if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex) var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url); if (hasPlaceholder) dataType = 'jsonp'; if (settings.cache === false || ( (!options || options.cache !== true) && ('script' == dataType || 'jsonp' == dataType) )) settings.url = appendQuery(settings.url, '_=' + Date.now()) function appendQuery(url, query) { if (query == '') return url; return (url + '&' + query).replace(/[&?]{1,2}/, '?') } if ('jsonp' == dataType) { if (!hasPlaceholder) settings.url = appendQuery(settings.url, settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') return $.ajaxJSONP(settings, deferred) }
若是咱們沒有傳入url,那麼url就是window.location.href。'#'表明網頁中的一個位置,右邊的字符,就是該位置的標識符,#是用來指導瀏覽器動做的,對瀏覽器沒有用,因此在截取url的時候,不必把後面的部分傳給服務器。hasPlaceholder這裏的正則表達式,用於匹配相似'?name=?'這種字符串,若是hasPlaceholder爲true,則dataType爲jsonp。下面的時不使用留在緩存的數據,第一種是設置cache爲false,或者dataType爲script和jsonp的狀況,須要在url後面添加事件。下面是appendQuery方法,就是把字符串拼接到url後邊,可是須要把'&'替換成'?'。若是咱們的請求是jsonp請求,須要在url後面添加一些callback=?這種參數。數組
var mime = settings.accepts[dataType], headers = {}, setHeader = function(name, value) {headers[name.toLowerCase()] = [name, value]}, protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, xhr = settings.xhr(), nativeSetHeader = xhr.setRequestHeader, abortTimeout if (deferred) deferred.promise(xhr); if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest'); setHeader('Accept', mime || '*/*') if (mime = settings.mimeType || mime) { if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] xhr.overrideMimeType && xhr.overrideMimeType(mime) }
settings中的accepts表示從服務器請求的MIME類型,指定dataType的值,包括script、json、xml、html、text。mime存儲的就是相似'application/json'的字符串。protocol就是匹配我們相似'http://'的這種協議。
若是deferred存在,就把xhr轉換爲deferred對象。接着看下去,若是不是跨域的,那就是ajax請求。而後後面的判斷條件查了一下是針對一些mozillar瀏覽器進行修正(有個問題就是他們上哪兒知道的用這種方式來修正啊)。promise
xhr.onreadystatechange = function() { if (xhr.readyState == 4) { xhr.onreadystatechange = empty if (/*若是成功*/){ // 對返回結果進行處理 } } } xhr.open(settings.type, settings.url, async, settings.username, settings.password)
沒什麼難的,就是根據傳入的參數不一樣進行處理。瀏覽器
這個函數的做用在於序列化傳入對象,下面是他的代碼:緩存
$.param = function (obj, traditional) { var params = [] params.add = function (key, value) { if ($.isFunction(value)) value = value() if (value == null) value = "" this.push(escape(key) + '=' + escape(value)) } serialize(params, obj, traditional) return params.join('&').replace(/%20/g, '+') }
傳入一個對象,和一個標記,這個traditional表示激活傳統的方式經過$.param來獲得data。首先定義一個空數組,若是在上面添加方法,這個方法的主要做用向params裏邊添加序列化的對象,escape=encodeURIComponent,而後調用serialize,將obj對象添加到params中,最後返回將params數組用'&'拼接,而後這裏的%20表示空格,意思是將空格替換成'+'服務器
下面是代碼
function serialize(params, obj, traditional, scope) { var type, array = $.isArray(obj), hash = $.isPlainObject(obj) $.each(obj, function (key, value) { type = $.type(value) if (scope) key = traditional ? scope : scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' // handle data in serializeArray() format if (!scope && array) params.add(value.name, value.value) // recurse into nested objects else if (type == "array" || (!traditional && type == "object")) serialize(params, value, traditional, key) else params.add(key, value) }) }
params是帶有add方法的數組,遍歷obj的鍵值對,屬性值多是對象也多是數組或者字符串分別進行處理。若是obj的某個屬性值是對象或者數組,scope就表明是該屬性,那麼這時候向params傳入的key就須要變化,變成相似'a[b]'這裏的b是obj的某個屬性值是對象的一個屬性。