Jquery源码分析---Ajax分析
2010-09-14 13:39:06 来源:WEB开发网getScript、getJSON都是在调用 jQuery.get,传入不同的datatype的形式来区别不同的类型。
7.2 jquery.ajax
这所有的最终都是通过jQuery.ajax()来完成的。
ajax : function(s) {
//两次继承s,以便在测试中能检测
s = jQuery.extend(true, s, jQuery.extend(true, {},
jQuery.ajaxSettings, s)); ①
var jsonp, jsre = /=?(&|$)/g, status, data,
type = s.type .toUpperCase();
// 如果不是字符集串就转换在查询字符集串
if (s.data && s.processData && typeof s.data != "string")
s.data = jQuery.param(s.data);
// 构建jsonp请求字符集串。jsonp是跨域请求,要加上callback=?后面将会加函数名
if (s.dataType == "jsonp") { ②
if (type == "GET") {//使get的url包含 callback=?后面将会进行加函数名
if (!s.url.match(jsre))
s.url += (s.url.match(/?/) ? "&" : "?")
+ (s.jsonp || "callback") + "=?";
} // 构建新的s.data,使其包含callback=function name
else if (!s.data || !s.data.match(jsre))
s.data = (s.data ? s.data + "&" : "") + (s.jsonp||"callback")+ "=?";
s.dataType = "json";
}
//判断是否为jsonp,如果是,进行处理。
if (s.dataType == "json"
&& (s.data && s.data.match(jsre) || s.url.match(jsre))) {③
jsonp = "jsonp" + jsc++;
/ /为请求字符集串的callback=加上生成回调函数名
if (s.data)s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
// 我们需要保证jsonp 类型响应能正确地执行
//jsonp的类型必须为script。这样才能执行服务器返回的
//代码。这里就是调用这个回调函数。
s.dataType = "script";
//window下注册一个jsonp回调函数有,让ajax请求返回的代码调用执行它,
//在服务器端我们生成的代码 如callbackname(data);形式传入data.
window[jsonp] = function(tmp) {
data = tmp;success();complete();
// 垃圾回收,释放联变量,删除jsonp的对象,除去head中加的script元素
window[jsonp] = undefined;
try { delete window[jsonp];
} catch (e) { }
if (head) head.removeChild(script);
};
}
if (s.dataType == "script" && s.cache == null) s.cache = false;
// 加上时间戳,可见加cache:false就会加上时间戳
if (s.cache === false && type == "GET") {
var ts = now();
var ret = s.url.replace(/(?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
// 没有代替,就追加在url的尾部
s.url = ret+ ((ret == s.url) ? (s.url.match(/?/) ? "&" : "?") + "_="
+ ts : "");
}
// data有效,追加到get类型的url上去
if (s.data && type == "GET") {
s.url += (s.url.match(/?/) ? "&" : "?") + s.data;
// 防止IE会重复发送get和post data
s.data = null;
}
// 监听一个新的请求
if (s.global && !jQuery.active++) jQuery.event.trigger("ajaxStart");④
// 监听一个绝对的url,和保存domain
var parts = /^(w+:)?//([^/?#]+)/.exec(s.url);
// 如果我们正在请求一个远程文档和正在load json或script通过get类型
//location是window的属性,通过和地址栏中的地址比较判断是不是跨域。
if (s.dataType == "script" && type == "GET"&& parts && (parts[1] &&
parts[1] != location.protocol || parts[2] != location.host)) {⑤
// 在head中加上<script src=""></script>
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = s.url;
if (s.scriptCharset) script.charset = s.scriptCharset;
//如果datatype不是jsonp,但是url却是跨域的。采用scriptr的
//onload或onreadystatechange事件来触发回调函数。
if (!jsonp) {
var done = false;
// 对所有浏览器都加上处理器
script.onload = script.onreadystatechange = function() {
if (!done&& (!this.readyState || this.readyState == "loaded"
|| this.readyState == "complete")) {
done = true; success();
complete();head.removeChild(script);
}
};
}
head.appendChild(script);
// 已经使用了script 元素注射来处理所有的事情
return undefined;
}
var requestDone = false;
// 创建request,IE7不能通过XMLHttpRequest来完成,只能通过ActiveXObject
var xhr = window.ActiveXObject ⑥
? new ActiveXObject("Microsoft.XMLHTTP"): new XMLHttpRequest();
// 创建一个请求的连接,在opera中如果用户名为null会弹出login窗口中。
if (s.username)xhr.open(type, s.url, s.async, s.username, s.password);
else xhr.open(type, s.url, s.async);
// try/catch是为防止FF3在跨域请求时报错
try {// 设定Content-Type ⑦
if (s.data)
xhr.setRequestHeader("Content-Type", s.contentType);
// 设定If-Modified-Since
if (s.ifModified)
xhr.setRequestHeader("If-Modified-Since",
jQuery.lastModified[s.url]|| "Thu, 01 Jan 1970 00:00:00 GMT");
// 这里是为了让服务器能判断这个请求是XMLHttpRequest
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// 设定 Accepts header 。指能接收的content-type,在服务器端设定
xhr.setRequestHeader("Accept", s.dataType && s.accepts[s.dataType]
? s.accepts[s.dataType] + ", */*": s.accepts._default);
} catch (e) {}
//拦截方法,我们可以在send之前进行拦截。返回false就不send
if (s.beforeSend && s.beforeSend(xhr, s) === false) { ⑧
// 清除active 请求计数
s.global && jQuery.active--;
xhr.abort();
return false;
}
// 触发全局的ajaxSend事件
if (s.global) jQuery.event.trigger("ajaxSend", [xhr, s]);
// 等待response返回,主要是为后面setInterval用。
var onreadystatechange = function(isTimeout) { ⑨
// 接收成功或请求超时
if (!requestDone && xhr&& (xhr.readyState == 4 ||isTimeout == "timeout")) { requestDone = true;
//清除定时器
if (ival) {clearInterval(ival); ival = null; }
// 分析status:tiemout-->error-->notmodified-->success
status = isTimeout == "timeout" ? "timeout" : !jQuery
ttpSuccess(xhr) ? "error" : s.ifModified&& jQuery.
httpNotModified(xhr, s.url) ? "notmodified": "success";
//如果success且返回了数据,那么分析这些数据
if (status == "success") {
try { data = jQuery.httpData(xhr, s.dataType, s);
} catch (e) { status = "parsererror"; }
}
// 分析数据成功之后,进行last-modified和success的处理。
if (status == "success") {
var modRes;
try {modRes = xhr.getResponseHeader("Last-Modified");
} catch (e) { //FF中如果head取不到,会抛出异常}
//保存last-mordified的标识。
if (s.ifModified && modRes)jQuery.lastModified[s.url] = modRes;
// JSONP 有自己的callback
if (!jsonp) success();
} else // 失败时的处理
jQuery.handleError(s, xhr, status);
// 无论如何都进行cpmplate.timeout和接收成功
complete();
if (s.async) xhr = null; // 防内存泄漏
}
};
if (s.async) {
// 这里是采用poll的方式,不是push的方式
//这里为什么不采用onreadystatechange?
var ival = setInterval(onreadystatechange, 13);
//如果过了timeout还没有请求到,会中断请求的。
if (s.timeout > 0)
setTimeout(function() {
if (xhr) { xhr.abort();
if (!requestDone) onreadystatechange("timeout"); }
}, s.timeout);
}
// 发送
try {xhr.send(s.data); catch(e){jQuery.handleError(s,xhr,null,e);} ⑩
// firefox 1.5 doesn't fire statechange for sync requests
if (!s.async) onreadystatechange();
function success() {
// 调用构建请求对象时指定的success回调。
if (s.success) s.success(data, status);
// 执行全局的回调
if (s.global) jQuery.event.trigger("ajaxSuccess", [xhr, s]);
}
function complete() {
// 本地的回调
if (s.complete) s.complete(xhr, status);
// 执行全局的回调
if (s.global) jQuery.event.trigger("ajaxComplete", [xhr, s]);
// 全局的ajax计数器
if (s.global && !--jQuery.active)jQuery.event.trigger("ajaxStop");
}
// return XMLHttpRequest便进行about()或其它操作.
return xhr;
},
更多精彩
赞助商链接