zepto源码学习-05 ajax
学习zeptoajax之前需要先脑补下,强烈推荐此文http://www.cnblogs.com/heyuquan/archive/2013/05/13/js-jquery-ajax.html
还有Aaron 的jquery源码分析系列,讲jquery的ajax的部分,当然其他也可以看,很值得学习。
zepto ajax部分的设计相对简单,毕竟zepto就是轻量级的库,没有jqeury那样复杂,jquery ajax是依赖于Deferred模块的,整个代码一千多行。而zepto只有几百行,整体设计相对简单,ajax部分可以不依赖于Deferred模块,独立运行。zepto ajax的使用相见 地址,先看懂使用然后再分析具体实现。
先看一个demo
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Ajax</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
<meta content="telephone=no" name="format-detection">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="description" content="">
<meta name="keywords" content="">
<style type="text/css" media="screen">
button{
width:150px;
height:100px;
line-height:100px;
margin:5px;
}
</style>
</head>
<body>
<button type="" id="btnTest">testajax</button>
<script type="text/javascript" src="../zepto-full-1.1.6.js"></script>
<script>
// 全局事件
$(document).on('ajaxStart',function (e) {
log('触发ajaxStart回调函数');
}).on('ajaxSend',function (e) {
log('触发ajaxSend回调函数');
}).on('ajaxBeforeSend',function (e) {
log('触发ajaxBeforeSend回调函数');
}).on('ajaxSuccess',function (e, jqXHR, s, data) {
log('触发ajaxSuccess回调函数');
}).on('ajaxError',function (e, jqXHR, s, errorData) {
log('触发ajaxError回调函数');
}).on('ajaxComplete',function (e, jqXHR, s) {
log('触发ajaxComplete回调函数');
}).on('ajaxStop',function (e) {
log('触发ajaxStop回调函数');
}); $('#btnTest').on('click',bindLocalEvent);
// 局部事件
function bindLocalEvent(e) {
var textareaid ='txt_event';
$.ajax({
//跨域地址http://192.168.11.198:8080/myTest/test/ajax-page.php
//ajax.php
url:'ajax.php',//ajax.php
type: 'get',
dataType: 'json',//text、json、jsonp
data:{
msg:'testtest',
error:3
},
//global: true,
//cache: false,
//jsonpCallback:log2,
beforeSend: function (jqXHR, s) {
log('触发beforeSend回调函数');
},
//zepto 不支持
dataFilter: function (data, dataType) {
log('触发dataFilter回调函数');
return data;
},
success: function (data, statusText, jqXHR) {
log('触发success回调函数');
},
error: function (jqXHR, textStatus, errorThrown) {
log('触发error回调函数');
},
complete: function (jqXHR, textStatus) {
log('触发complete回调函数');
}}).done(function(){
console.log('触发ajax done',this,[].slice.call(arguments));
});
} function log(txt) {
console.log(txt);
}
function log2(txt) {
console.log('------------jsonpCallback------------------');
//return function name
return 'log3';
}
function log3(txt) {
console.log('------------jsonpCallback done------------------');
}
</script>
</body>
</html>
ajax demo
点击按钮,控制台输出如下

以上图片基本上展示了正常情况下zepto ajax的处理流程,测试代码我是引入了Deferred模块的,所以可以链式调用done(后续会说)。
XHR那行输出是我默认开启chrome的Log XMLHttpRequests,所有的XMLHttpRequests都会在控制台输出。

再看一个发生错误的输出,一旦请求发生错误,后续的处理流程将发生变化。

再看跨域时候的情况,这个测试我指定了jsonpCallback为log2,dataType为jsonp,地址为http://192.168.11.198:8080/myTest/test/ajax-page.php。ajax请求部分参数如下
url:'http://192.168.11.198:8080/myTest/test/ajax-page.php',//ajax.php
type: 'get',
dataType: 'jsonp',//text、json、jsonp
data:{
msg:'testtest',
error:3
},
jsonpCallback:log2,
对应的php代码如下
<?php
$status = $_REQUEST['error'] ? 1 : 0;
$message = $status == 1 ? "验证码好像不对哦" : "SUCCESS";
if (!empty($_REQUEST['msg'])) {
$msg=$_REQUEST['msg'];
$message = $msg;
$status = $_REQUEST['error'];
}
$d=array("result"=>$status,"message"=>$message,"status"=>$_REQUEST['error']);
$c=$_REQUEST['callback'];
$rd;
if ($c===null || $c==='') {
echo json_encode($d);
}else{
$rd=$c+"("+json_encode($d)+")";
echo $c."(".json_encode($d).")";
}
?>
php代码主要是取到callback,最后把数据组装为 callback(data)的形势返回。
此时页面控制台输入如下

$.get、$.getJSON、$.post、load最后都调用了$.ajax,
$.ajax(options) 内部处理流程。
把$.ajaxSettings[key]中的设置赋值到options中
执行全局ajaxStart(settings)
判断当前请求的url是否跨域
if (!settings.crossDomain) {
urlAnchor = document.createElement('a')
urlAnchor.href = settings.url
urlAnchor.href = urlAnchor.href
//内部判断当前的url是否跨域
settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host)
}
没有指定url默认给定当前页面url===》serializeData(settings)处理settings里面的 data,其实就是对data做相关转换
再次根据url判断dataType的类型,如果dataType是jsonp类型,就执行$.ajaxJSONP,并且返回,稍后看ajaxJSONP的实现
var dataType = settings.dataType,
//如果请求的是jsonp,则将地址栏里的=?替换为callback=?,相当于一个简写
hasPlaceholder = /\?.+=\?/.test(settings.url)
if (hasPlaceholder) dataType = 'jsonp' if (settings.cache === false || (
(!options || options.cache !== true) &&
('script' == dataType || 'jsonp' == dataType)
))
//不缓存,在url后面添加时间戳
settings.url = appendQuery(settings.url, '_=' + Date.now()) //针对jsonp进行处理
if ('jsonp' == dataType) {
if (!hasPlaceholder)
settings.url = appendQuery(settings.url, settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
return $.ajaxJSONP(settings, deferred)
}
if (deferred) deferred.promise(xhr) 如果引入了Deferred这里会执行deferred.promise(xhr)。这句话的里面其实是
promise: function(obj) {
return obj != null ? $.extend(obj, promise) : promise
}
把当前deferred的一些行为附加到了xhr对象上,通过 deferred.promise() 方法返回的 deferred promise 对象,是没有 resolve ,reject, progress , resolveWith, rejectWith , progressWith 这些可以改变状态的方法,只能使用 done, then ,fail 等方法添加 handler 或者判断状态。所以返回xhr对象后面只能链式调用 done, then ,fail,且不能改变当前Deferred的状态。那什么时候改变Deferred的状态呢,其实是内部执行ajaxSuccess、ajaxError方法时,内部deferred对象调用resolveWith、rejectWith,改变deferred的状态,一个执行成功resolveWith,一个执行失败rejectWith。
function ajaxSuccess(data, xhr, settings, deferred) {
var context = settings.context,
status = 'success'
//获取上下文,调用success
settings.success.call(context, data, status, xhr)
//如果引用了Deferred模块,这里执行成功的通知,并传递相关参数
if (deferred) deferred.resolveWith(context, [data, status, xhr])
//触发全局的成功
triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
//调用ajaxComplete
ajaxComplete(status, xhr, settings)
}
// type: "timeout", "error", "abort", "parsererror"
function ajaxError(error, type, xhr, settings, deferred) {
var context = settings.context
//获取上下文,调用error
settings.error.call(context, xhr, type, error)
//如果引用了Deferred模块,这里执行失败的通知,并传递相关参数
if (deferred) deferred.rejectWith(context, [xhr, type, error])
//触发全局的失败
triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type])
//调用ajaxComplete
ajaxComplete(type, xhr, settings)
}
如果我们设置了超时时间、超时之后就会调用xhr.abort取消异步请求、并触发ajaxError
//当设置了settings.timeout,则在超时后取消请求,并执行timeout事件处理函数,并处罚error
if (settings.timeout > 0) abortTimeout = setTimeout(function() {
xhr.onreadystatechange = empty
xhr.abort()
ajaxError(null, 'timeout', xhr, settings, deferred)
}, settings.timeout)
xhr.readyState == 4,readyState有五种状态:、01、2、3、4;当值为4的时候,表示数据接收完毕,此时可以通过responseXml和responseText获取完整的回应数据。
判断处理状态,拿到相关数据,调用相关的处理函数。
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
xhr.onreadystatechange = empty
//清楚setTimeout
clearTimeout(abortTimeout)
var result, error = false
//根据状态来判断请求是否成功
//状态>=200 && < 300 表示成功
//状态 == 304 表示文件未改动过,也可认为成功
//如果是取要本地文件那也可以认为是成功的,xhr.status == 0是在直接打开页面时发生请求时出现的状态,也就是不是用localhost的形式访问的页面的情况
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
//取到dataType
dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
//取到返回的数据
result = xhr.responseText
try {
// http://perfectionkills.com/global-eval-what-are-the-options/
if (dataType == 'script')(1, eval)(result)
else if (dataType == 'xml') result = xhr.responseXML
else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
} catch (e) {
error = e
}
//调用ajaxError
if (error) ajaxError(error, 'parsererror', xhr, settings, deferred)
else ajaxSuccess(result, xhr, settings, deferred)
} else {
ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
}
}
}
$.ajaxJSONP、jsonp的处理
内部处理就是创建一个script标签去请求,对传入的callback做了一些处理,重写了callback
window[callbackName] = function() {
responseData = arguments
}
最终执行的时候,先是拿到返回的数据,赋值给内部变量responseData。在load或者error的时候,执行响应函数,先是移除创建script对象上的事件数据,然后从页面上移除这个对象。然后调用调用ajaxError或者ajaxSuccess,最后调用最初传入的callback
$.ajaxJSONP = function(options, deferred) {
//没有指定type
if (!('type' in options)) return $.ajax(options)
var _callbackName = options.jsonpCallback,
//调用options.jsonpCallback,得到返回的字符串。
//如果没有任何返回就生成一个callbackName=jsonpXXX.
callbackName = ($.isFunction(_callbackName) ?
_callbackName() : _callbackName) || ('jsonp' + (++jsonpID)),
//创建一个script标签,这里貌似没有jq的简洁啊
script = document.createElement('script'),
//保存最初指定的window[callbackName] callback
//如果调用options.jsonpCallback,但都没返回这里就是随机生成的jsonpXXX函数名,window[callbackName]应该是不存在的
originalCallback = window[callbackName],
responseData,
abort = function(errorType) {
$(script).triggerHandler('error', errorType || 'abort')
},
xhr = {
abort: abort
},
abortTimeout
if (deferred) deferred.promise(xhr)
$(script).on('load error', function(e, errorType) {
clearTimeout(abortTimeout)
//销毁事件,移除元素
$(script).off().remove()
if (e.type == 'error' || !responseData) {
ajaxError(null, errorType || 'error', xhr, options, deferred)
} else {
ajaxSuccess(responseData[0], xhr, options, deferred)
}
//赋值回原来的callback
window[callbackName] = originalCallback
//最初保存的window[callbackName]
if (responseData && $.isFunction(originalCallback))
//调用
originalCallback(responseData[0])
//销毁
originalCallback = responseData = undefined
})
if (ajaxBeforeSend(xhr, options) === false) {
abort('abort')
return xhr
}
//生成的jsonpXXX函数,指定callback=jsonpXXX。 请求成功返回,就会调用这个函数,给responseData赋值
//之前originalCallback = window[callbackName] 保存过了,这里重新指定
window[callbackName] = function() {
responseData = arguments
}
//处理简写 callback的情况
script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName)
//append到页面上
document.head.appendChild(script)
//处理超时的情况
if (options.timeout > 0) abortTimeout = setTimeout(function() {
abort('timeout')
}, options.timeout)
return xhr
}
代码简单,看完api,然后组合相关情况去测试,打断点一步一步看内部的处理,很多还得自己去领悟。最后附上全部注释代码
;(function($) {
var jsonpID = 0,
document = window.document,
key,
name,
rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
scriptTypeRE = /^(?:text|application)\/javascript/i,
xmlTypeRE = /^(?:text|application)\/xml/i,
jsonType = 'application/json',
htmlType = 'text/html',
blankRE = /^\s*$/,
originAnchor = document.createElement('a')
originAnchor.href = window.location.href
/*
触发ajaxStart回调函数 testAjax.html:109
触发beforeSend回调函数 testAjax.html:109
触发ajaxBeforeSend回调函数 testAjax.html:109
触发ajaxSend回调函数 testAjax.html:109
XHR finished loading: GET "http://192.168.11.198:8080/sourceCode/zepto/testPage/ajax.php?_=1420612854381". zepto-full-1.1.6.js:1403
触发success回调函数 testAjax.html:109
触发ajax done
触发ajaxSuccess回调函数 testAjax.html:109
触发complete回调函数 testAjax.html:109
触发ajaxComplete回调函数 testAjax.html:109
触发ajaxStop回调函数
*/
// trigger a custom event and return false if it was cancelled
function triggerAndReturn(context, eventName, data) {
//初始化一个Event对象
var event = $.Event(eventName)
//触发context这个对象的该事件
$(context).trigger(event, data)
//返回是否调用过阻止默认行为,初始为false,掉用过就是true
return !event.isDefaultPrevented()
}
//触发 ajax的全局事件
// trigger an Ajax "global" event
function triggerGlobal(settings, context, eventName, data) {
//默认context为document
if (settings.global) return triggerAndReturn(context || document, eventName, data)
}
// Number of active Ajax requests
$.active = 0
function ajaxStart(settings) {
//设置了global 为true
if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart')
}
function ajaxStop(settings) {
if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop')
}
// triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
function ajaxBeforeSend(xhr, settings) {
var context = settings.context
//先触发beforeSend 再触发ajaxBeforeSend ,其中一个返回false就停止往下执行
if (settings.beforeSend.call(context, xhr, settings) === false ||
triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
return false
//触发ajaxSend
triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
}
function ajaxSuccess(data, xhr, settings, deferred) {
var context = settings.context,
status = 'success'
//获取上下文,调用success
settings.success.call(context, data, status, xhr)
//如果引用了Deferred模块,这里执行成功的通知,并传递相关参数
if (deferred) deferred.resolveWith(context, [data, status, xhr])
//触发全局的成功
triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
//调用ajaxComplete
ajaxComplete(status, xhr, settings)
}
// type: "timeout", "error", "abort", "parsererror"
function ajaxError(error, type, xhr, settings, deferred) {
var context = settings.context
//获取上下文,调用error
settings.error.call(context, xhr, type, error)
//如果引用了Deferred模块,这里执行失败的通知,并传递相关参数
if (deferred) deferred.rejectWith(context, [xhr, type, error])
//触发全局的失败
triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type])
//调用ajaxComplete
ajaxComplete(type, xhr, settings)
}
// status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
function ajaxComplete(status, xhr, settings) {
var context = settings.context
//调用setting里面的complete
settings.complete.call(context, xhr, status)
//触发ajaxComplete
triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
//调用ajaxStop
ajaxStop(settings)
}
// Empty function, used as default callback
function empty() {}
$.ajaxJSONP = function(options, deferred) {
//没有指定type
if (!('type' in options)) return $.ajax(options)
var _callbackName = options.jsonpCallback,
//调用options.jsonpCallback,得到返回的字符串。
//如果没有任何返回就生成一个callbackName=jsonpXXX.
callbackName = ($.isFunction(_callbackName) ?
_callbackName() : _callbackName) || ('jsonp' + (++jsonpID)),
//创建一个script标签,这里貌似没有jq的简洁啊
script = document.createElement('script'),
//保存最初指定的window[callbackName] callback
//如果调用options.jsonpCallback,但都没返回这里就是随机生成的jsonpXXX函数名,window[callbackName]应该是不存在的
originalCallback = window[callbackName],
responseData,
abort = function(errorType) {
$(script).triggerHandler('error', errorType || 'abort')
},
xhr = {
abort: abort
},
abortTimeout
if (deferred) deferred.promise(xhr)
$(script).on('load error', function(e, errorType) {
clearTimeout(abortTimeout)
//销毁事件,移除元素
$(script).off().remove()
if (e.type == 'error' || !responseData) {
ajaxError(null, errorType || 'error', xhr, options, deferred)
} else {
ajaxSuccess(responseData[0], xhr, options, deferred)
}
//赋值回原来的callback
window[callbackName] = originalCallback
//最初保存的window[callbackName]
if (responseData && $.isFunction(originalCallback))
//调用
originalCallback(responseData[0])
//销毁
originalCallback = responseData = undefined
})
if (ajaxBeforeSend(xhr, options) === false) {
abort('abort')
return xhr
}
//生成的jsonpXXX函数,指定callback=jsonpXXX。 请求成功返回,就会调用这个函数,给responseData赋值
//之前originalCallback = window[callbackName] 保存过了,这里重新指定
window[callbackName] = function() {
responseData = arguments
}
//处理简写 callback的情况
script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName)
//append到页面上
document.head.appendChild(script)
//处理超时的情况
if (options.timeout > 0) abortTimeout = setTimeout(function() {
abort('timeout')
}, options.timeout)
return xhr
}
//默认的setting
$.ajaxSettings = {
// Default type of request
type: 'GET',
// Callback that is executed before request
beforeSend: empty,
// Callback that is executed if the request succeeds
success: empty,
// Callback that is executed the the server drops error
error: empty,
// Callback that is executed on request complete (both: error and success)
complete: empty,
// The context for the callbacks
context: null,
// Whether to trigger "global" Ajax events
global: true,
// Transport
xhr: function() {
return new window.XMLHttpRequest()
},
// MIME types mapping
// IIS returns Javascript as "application/x-javascript"
accepts: {
script: 'text/javascript, application/javascript, application/x-javascript',
json: jsonType,
xml: 'application/xml, text/xml',
html: htmlType,
text: 'text/plain'
},
// Whether the request is to another domain
crossDomain: false,
// Default timeout
timeout: 0,
// Whether data should be serialized to string
processData: true,
// Whether the browser should be allowed to cache GET responses
cache: true
}
function mimeToDataType(mime) {
if (mime) mime = mime.split(';', 2)[0]
return mime && (mime == htmlType ? 'html' :
mime == jsonType ? 'json' :
scriptTypeRE.test(mime) ? 'script' :
xmlTypeRE.test(mime) && 'xml') || 'text'
}
//参数拼接
function appendQuery(url, query) {
if (query == '') return url
return (url + '&' + query).replace(/[&?]{1,2}/, '?')
}
// serialize payload and append it to the URL for GET requests
function serializeData(options) {
//要处理data,data存在、不是string类型
if (options.processData && options.data && $.type(options.data) != "string")
options.data = $.param(options.data, options.traditional)
if (options.data && (!options.type || options.type.toUpperCase() == 'GET'))
options.url = appendQuery(options.url, options.data), options.data = undefined
}
$.ajax = function(options) {
//处理外出传入的options,如果加载了Deferred 模块,这里创建一个Deferred实例对象
var settings = $.extend({}, options || {}),
deferred = $.Deferred && $.Deferred(),
urlAnchor
//把settings中没有$.ajaxSettings中的设置 都给赋值到settings里面
for (key in $.ajaxSettings)
if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
//调用ajaxStart
ajaxStart(settings)
//如果不是跨域
if (!settings.crossDomain) {
urlAnchor = document.createElement('a')
urlAnchor.href = settings.url
urlAnchor.href = urlAnchor.href
//内部判断当前的url是否跨域
settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host)
}
//没有设置url 默认为当前页面的地址
if (!settings.url) settings.url = window.location.toString()
//处理里面的data
serializeData(settings)
var dataType = settings.dataType,
//如果请求的是jsonp,则将地址栏里的=?替换为callback=?,相当于一个简写
hasPlaceholder = /\?.+=\?/.test(settings.url)
if (hasPlaceholder) dataType = 'jsonp'
if (settings.cache === false || (
(!options || options.cache !== true) &&
('script' == dataType || 'jsonp' == dataType)
))
//不缓存,在url后面添加时间戳
settings.url = appendQuery(settings.url, '_=' + Date.now())
//针对jsonp进行处理
if ('jsonp' == dataType) {
if (!hasPlaceholder)
settings.url = appendQuery(settings.url, settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
return $.ajaxJSONP(settings, deferred)
}
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
//引入了Deferred没款,这里xhr就附加了promise对象的相关行为,最后返回的是一个promise对象,后续就可以链式done
if (deferred) deferred.promise(xhr)
//设置header
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)
}
//设置contentType
if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded')
if (settings.headers)
for (name in settings.headers) setHeader(name, settings.headers[name])
xhr.setRequestHeader = setHeader
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
xhr.onreadystatechange = empty
//清楚setTimeout
clearTimeout(abortTimeout)
var result, error = false
//根据状态来判断请求是否成功
//状态>=200 && < 300 表示成功
//状态 == 304 表示文件未改动过,也可认为成功
//如果是取要本地文件那也可以认为是成功的,xhr.status == 0是在直接打开页面时发生请求时出现的状态,也就是不是用localhost的形式访问的页面的情况
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
//取到dataType
dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
//取到返回的数据
result = xhr.responseText
try {
// http://perfectionkills.com/global-eval-what-are-the-options/
if (dataType == 'script')(1, eval)(result)
else if (dataType == 'xml') result = xhr.responseXML
else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
} catch (e) {
error = e
}
//调用ajaxError
if (error) ajaxError(error, 'parsererror', xhr, settings, deferred)
else ajaxSuccess(result, xhr, settings, deferred)
} else {
ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
}
}
}
//ajaxBeforeSend 返回false
if (ajaxBeforeSend(xhr, settings) === false) {
xhr.abort()
ajaxError(null, 'abort', xhr, settings, deferred)
return xhr
}
//设置请求头信息
if (settings.xhrFields)
for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name]
var async = 'async' in settings ? settings.async : true
//发起请求
xhr.open(settings.type, settings.url, async, settings.username, settings.password)
for (name in headers) nativeSetHeader.apply(xhr, headers[name])
//当设置了settings.timeout,则在超时后取消请求,并执行timeout事件处理函数,并处罚error
if (settings.timeout > 0) abortTimeout = setTimeout(function() {
xhr.onreadystatechange = empty
xhr.abort()
ajaxError(null, 'timeout', xhr, settings, deferred)
}, settings.timeout)
// avoid sending empty string (#319)
xhr.send(settings.data ? settings.data : null)
//如果引入了Deferred模块这里返回promise对象
return xhr
}
//处理多个参数组合的情况
// handle optional data/success arguments
function parseArguments(url, data, success, dataType) {
if ($.isFunction(data)) dataType = success, success = data, data = undefined
if (!$.isFunction(success)) dataType = success, success = undefined
return {
url: url,
data: data,
success: success,
dataType: dataType
}
}
$.get = function( /* url, data, success, dataType */ ) {
return $.ajax(parseArguments.apply(null, arguments))
}
$.post = function( /* url, data, success, dataType */ ) {
var options = parseArguments.apply(null, arguments)
options.type = 'POST'
return $.ajax(options)
}
$.getJSON = function( /* url, data, success */ ) {
var options = parseArguments.apply(null, arguments)
options.dataType = 'json'
return $.ajax(options)
}
$.fn.load = function(url, data, success) {
if (!this.length) return this
var self = this,
parts = url.split(/\s/),
selector,
options = parseArguments(url, data, success),
callback = options.success
if (parts.length > 1) options.url = parts[0], selector = parts[1]
options.success = function(response) {
//创建一个div,把返回的字符串script替换掉,把放字符串入div中,通过选择器找到对应元素
self.html(selector ?
$('<div>').html(response.replace(rscript, "")).find(selector) : response)
//调用传入的回调函数
callback && callback.apply(self, arguments)
}
$.ajax(options)
return this
}
var escape = encodeURIComponent
function serialize(params, obj, traditional, scope) {
var type, array = $.isArray(obj),
hash = $.isPlainObject(obj)
$.each(obj, function(key, value) {
type = $.type(value)
//scope用作处理value也是object或者array的情况
//traditional表示是否以传统的方式拼接数据,
//传统的意思就是比如现有一个数据{a:[1,2,3]},转成查询字符串后结果为'a=1&a=2&a=3'
//非传统的的结果则是a[]=1&a[]=2&a[]=3
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
// 当value值是数组或者是对象且不是按传统的方式序列化的时候,需要再次遍历value
else if (type == "array" || (!traditional && type == "object"))
serialize(params, value, traditional, key)
else params.add(key, value)
})
}
$.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, '+')
}
})(Zepto);
zepto ajax
本文地址:http://www.cnblogs.com/Bond/p/4210808.html
zepto源码学习-05 ajax的更多相关文章
- zepto源码学习-06 touch
先上菜,看这个模块的最后一段代码,一看就明白. ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 't ...
- zepto源码学习-01-整体感知
在公司一直做移动端的项目,偶尔会做点PC端的东西,但基本上都是和移动端打交道. 移动端嘛必须上zepto,简单介绍下Zepto:它是一个面向高级浏览器的JavaScript框架的,实现JQuery的大 ...
- zepto源码学习-03 $()
在第一篇的时候提到过关于$()的用法,一个接口有很多重载,用法有很多种,总结了下,大概有一以下几种 1.$(selector,context?) 传入一个选择器返回一个zepto对象 2.$(func ...
- zepto源码学习-02 工具方法-详细解读
上一篇:地址 先解决上次留下的疑问,开始看到zepto.z[0]这个东西的时候,我很是不爽,看着它都不顺眼,怎么一个zepto的实例对象var test1=$('#items'); test__pr ...
- zepto源码学习-04 event
之前说完$(XXX),然后还有很多零零碎碎的东西需要去分析,结果一看代码,发现zepto的实现都相对简单,没有太多可分析的.直接略过了一些实现,直接研究Event模块,相比JQuery的事件系统,ze ...
- 非常适合新手的jq/zepto源码分析05
zepto的原型 $.fn 属性: constructor //构造行数 forEach: emptyArray.forEach, //都是原生数组的函数reduce: emptyArray.re ...
- zepto源码--核心方法10(位置)--学习笔记
今天基本上就是zepto学习笔记的最后一篇了,介绍一下有关位置的函数,position, offset, scrollLeft, scrollTop scrollLeft 如果所选取的包装集不存在,则 ...
- zepto源码--定义变量--学习笔记
主要了解一下zepto定义的初始变量. 逐一以自己的理解解析,待到后面完全透彻理解之后,争取再写一遍zepto源码学习的文章. 其中的undefined确实不明白为什么定义这么个变量在这里. docu ...
- 读Zepto源码之Ajax模块
Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
随机推荐
- 关于Java基本数据类型
Java的基本数据类型分为两大类:boolean类型和数字类型.而数值类型又分为整数类型和浮点类型.而整数类型中的字符类型也可以被单独对待. 总共是4类8种. byte类型整数在内存里占8位. -12 ...
- Matlab图像函数之pie
一.pie pie用于描绘平面饼图. (1)pie(X) 利用向量X中的数据描绘饼图. 例如: X = [1, 1, 2, 2, 3, 4, 5]; pie(X) 得到 注意,X中的数据被看做频数,饼 ...
- 【Slickflow学习】.NET开源工作流环境搭建(三)
第一次自己写博客文章,大家多多指教.写博客主要记录一下学习的过程,给初学者提供下参考,也留给自己做备忘. Slickflow .NET开源工作流-环境搭建 在VS2010中使用附加进程的方式调试IIS ...
- 优雅的使用 PhpStorm 来开发 Laravel 项目
[目录] Prerequisites plugin installation and configuration 1 Ensure Composer is initialized 2 Install ...
- files_dir
一.opendir() —— 打开目录 opendir( 打开的当前目录 ); 二.closedir() —— 关闭目录 三.readdir() —— 返回目录中的各个元素,返回上一个并且指向 ...
- 【高性能服务器】Tomcat剖析
引言 Tomcat是一个流行的servlet容器,对于开发人员来说整体和容器打交道有必要花一些时间爱你了解其内部结构.本文将从一下几个方面来剖析其内部结构. 整体结构 连接器 初始化过程 如何处理一个 ...
- Android Studio如何显示行号
Android Studio默认没有显示行号,很多同学在使用中很不习惯.本经验介绍怎样让Android Studio显示行号. 首先我们打开我们下载安装好的Android Studio 然后右击工具按 ...
- SQL Server调优系列进阶篇 - 如何索引调优
前言 上一篇我们分析了数据库中的统计信息的作用,我们已经了解了数据库如何通过统计信息来掌控数据库中各个表的内容分布.不清楚的童鞋可以点击参考. 作为调优系列的文章,数据库的索引肯定是不能少的了,所以本 ...
- 第十二篇、HTML常用框架收集
1.Swiper 广告轮播插件 2.Bootstrap 响应式布局 3.jQuery js兼容插件 4.jQuery Mobile 5.phoneGap
- ios 获取字符串所需要占用的label的高度
// 设置字体大小 UIFont *fnt=[UIFont systemFontOfSize:16]; NSDictionary *attribute = @{NSFontAttributeNa ...