框架的作用就是简化我们做的事情,却又不失灵活性。jquery是js框架中的中流砥柱,灵活并且强大。

jquery中对ajax的封装很完美,且不说底层的ajax函数的强大,但是其上层的get ,post ,getscript ,getJson 基本对各种应用游刃有余。为什么要看源码,一是闲着蛋疼,二是为了在出问题时,能找出问题所在。三是……。

jquery中有一个对象ajaxSeetings ,ajax请求最基本的配置就在这里,结构如下

ajaxSettings: { url: location.href, global: true, type: "GET", contentType: "application/x-www-form-urlencoded", processData: true, async: true, /* timeout: 0, data: null, username: null, password: null, traditional: false, */ // Create the request object; Microsoft failed to properly // implement the XMLHttpRequest in IE7 (can't request local files), // so we use the ActiveXObject when it is available // This function can be overriden by calling jQuery.ajaxSetup xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ? function() { return new window.XMLHttpRequest(); } : function() { try { return new window.ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {} }, accepts: { xml: "application/xml, text/xml", html: "text/html", script: "text/javascript, application/javascript", json: "application/json, text/javascript", text: "text/plain", _default: "*/*" } }

基本上名字就能代表它的配置项目,processData可能比较陌生。我们在使用get以及其他上层函数请求资源时,传递一个key/value的对象。例如$.get(“xxxx”,{name:’pr’,password:’pr’} ,  ……); 如果process设置为true,{name:’pr’,password:’pr’}就会转换为name=pr&password=pr;这样在后面如果ajax方式为get则会将装换的字符串附加到url后面,如果设置为false则不进行此转换,默认是true,也最好不要改。值得一看内容当然是属性xhr,这个属性是个函数,当然函数最后都会返回浏览器兼容的XmlHttpRequest对象。整个ajax的核心操作对象就是它,这就免去了我们自己构造XmlHttpRequest对象时考虑兼容问题的纠结。

ajax: function( origSettings ),ajax接受一个配置对象,就跟上面的ajaxSettings那样的结构,

var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings); var jsonp, status, data, callbackContext = origSettings && origSettings.context || s, type = s.type.toUpperCase();   // convert data if not already a string if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); }

首先函数将默认配置和传进来的配置进行合并,在函数中注意有个{},这样合并就不会影响ajaxSettings 和originSettings的本来的值。CallbackContext是执行ajax回调函数是函数的上下文。其他不多说。然后根据data,ProcessData 和data是否是string来决定是不是要将data对象转换为参数形式字符串。jquery.param是个工具函数,traditional用来决定是不是要进行深层次遍历以生成参数字符串。具体事例见jquery文档。

// Handle JSONP Parameter Callbacks if ( s.dataType === "jsonp" ) { if ( type === "GET" ) { if ( !jsre.test( s.url ) ) { s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?"; } } else if ( !s.data || !jsre.test(s.data) ) { s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?"; } s.dataType = "json"; } // Build temporary JSONP function if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) { jsonp = s.jsonpCallback || ("jsonp" + jsc++); // Replace the =? sequence both in the query string and the data if ( s.data ) { s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1"); } s.url = s.url.replace(jsre, "=" + jsonp + "$1"); // We need to make sure // that a JSONP style response is executed properly s.dataType = "script"; // Handle JSONP-style loading window[ jsonp ] = window[ jsonp ] || function( tmp ) { data = tmp; success(); complete(); // Garbage collect window[ jsonp ] = undefined; try { delete window[ jsonp ]; } catch(e) {} if ( head ) { head.removeChild( script ); } }; }

接下来特殊处理请求数据类型为jsonp,jsonp其实是一种非官方的协议,主要就是跨域的访问。在后面你可以看到它会和请求类型为‘script’相似处理。

如果jsonp大家不熟悉的话,可以去网上查找相关资料,或直接跳过jsonp,不影响后面的阅读。

jsre是一个正则表达式  jsre = /=\?(&|$)/  ,他主要又来匹配‘=?’这个其实是jsonp请求中独有的字符串,如果url中没有对应的字符,则在后面加上jsonp请求独有的字符串。requery同样也是一个正则表达式/\?/ ,就是用来匹配问号的,如果原先url含有?,则说明url中带有参数,则连接字符使用&,否则用?。如果配置对象中含有jsonp,则指定了jsonp的回调函数名,否则使用默认回调函数名callback。若果ajax请求采用post方式,则只需对配置对象中的data进行拼接字符串即可。datatype设置为json是为了进一步的细化处理。无论是在get方式还是其他方式,在处理前都会用jsre匹配一下,只有在确保字符串中没有jsonp的特征字符串’=?‘时才会就行处理,也就是说不能有两个jsonp在一起(自己的一点瞎想,欢迎大家讨论)。

接下来构建jsonp回调函数。因为前文说过没有指定jsonp属性的话是默认为Callback。如果指定了jsonpCallback则直接用但是没有的就要构造构造一个独一无二的回调函数名,用什么呢,除了时间还有跟好的解决方法吗?jsc就是当前时间 ,jsc =now(); 然后用此回调函数名换掉?,这样就符合了参数字符串的格式。

window[jsonp],为回调函数注册。

if ( s.dataType === "script" && s.cache === null ) { s.cache = false; }   if ( s.cache === false && type === "GET" ) { var ts = now();   // try replacing _= if it is there var ret = s.url.replace(rts, "$1_=" + ts + "$2");   // if nothing was replaced, add timestamp to the end s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : ""); }   // If data is available, append data to url for get requests if ( s.data && type === "GET" ) { s.url += (rquery.test(s.url) ? "&" : "?") + s.data; }   // Watch for a new set of requests if ( s.global && ! jQuery.active++ ) { jQuery.event.trigger( "ajaxStart" ); }   // Matches an absolute URL, and saves the domain var parts = rurl.exec( s.url ), remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);   // If we're requesting a remote document // and trying to load JSON or Script with a GET if ( s.dataType === "script" && type === "GET" && remote ) { var head = document.getElementsByTagName("head")[0] || document.documentElement; var script = document.createElement("script"); script.src = s.url; if ( s.scriptCharset ) { script.charset = s.scriptCharset; }   // Handle Script loading if ( !jsonp ) { var done = false;   // Attach handlers for all browsers script.onload = script.onreadystatechange = function() { if ( !done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) { done = true; success(); complete();   // Handle memory leak in IE script.onload = script.onreadystatechange = null; if ( head && script.parentNode ) { head.removeChild( script ); } } }; }   // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709 and #4378). head.insertBefore( script, head.firstChild );   // We handle everything using the script element injection return undefined; }

进行ajax进行请求是可能会会有缓存文件(当然着仅存在请求方式为Get时,关于Get和post在ajax请求时的具体区别请参考w3school中的介绍),当请求格式为‘script’(可能是后来转换的如jsonp)时,则没有缓存文件,实现方式是为每一次请求加一个时间戳,ts也是now,同jsc一样。

如果请求方式为Get,将请求参数加到url后面。

下面介绍一下ajax中自定义事件 这是官方的ajax事件列表(自己后面有对应的翻译,水品有限)

ajaxStart (Global Event)
This event is broadcast if an Ajax request is started and no other Ajax requests are currently running.

  • (此事件只有当当前有一个ajax请求已开始且当前没有其他ajax请求正在运行时触发)
  • beforeSend (Local Event)
    This event, which is triggered before an Ajax request is started, allows you to modify the XMLHttpRequest object (setting additional headers, if need be.)
  • (此事件在一个ajax请求开始前触发。让你可以修改请求对象(如设置其他头))
  • ajaxSend (Global Event)
  • This global event is also triggered before the request is run.
  • (此事件在在请求运行前触发)
  • success (Local Event)
    This event is only called if the request was successful (no errors from the server, no errors with the data).
  • (此事件只有放请求成功时触发(没有从服务器返回任何错误,返回数据没有任何错误))
  • ajaxSuccess (Global Event)
    This event is also only called if the request was successful.
  • (此事件在请求成功时触发)
  • error (Local Event)
    This event is only called if an error occurred with the request (you can never have both an error and a success callback with a request).
  • 当请求错误时触发(一个请求不可能同时触发error和success两个回调函数)
  • ajaxError (Global Event)
    This global event behaves the same as the local error event.
  • 同上
  • complete (Local Event)
    This event is called regardless of if the request was successful, or not. You will always receive a complete callback, even for synchronous requests.
  • 无论请求成功与否都会触发
  • ajaxComplete (Global Event)
    This event behaves the same as the complete event and will be triggered every time an Ajax request finishes.
  • 同上

ajaxStop (Global Event)
This global event is triggered if there are no more Ajax requests being processed. 当前没有请求处理是触发

上面都列出了这些事件的触发条件。当符合这些条件是就要trigger这些事件,至于有没有注册处理函数那就是用户的事情。

参照上述的这些触发条件,可以很了解ajax函数中对这些事件的触发。要说的是jquery.active的初始值是0;

rurl = /^(\w+:)?\/\/([^\/?#]+)/ 主要又来将url的歇息名称 和主机名取出来。当当前请求是资源的协议名或主机名与与浏览器当前的对象内容不同,则为远程跨域访问,设置remote为true。

如果是请求远程资源且为Get,请求类型为script,则创建script的dom对象,并设置相关属性,已加载script脚本。最后设置script加载成功时的回调函数。

最后返回undefine,是处理script加载的最后步骤。如果加载的是script则到此处请求结束。

这里主要是处理了script和jsonp着两种数据类型的请求。

var requestDone = false;   // Create the request object var xhr = s.xhr();   if ( !xhr ) { return; }   // Open the socket // Passing null username, generates a login popup on Opera (#2865) if ( s.username ) { xhr.open(type, s.url, s.async, s.username, s.password); } else { xhr.open(type, s.url, s.async); }   // Need an extra try/catch for cross domain requests in Firefox 3 try { // Set the correct header, if data is being sent if ( s.data || origSettings && origSettings.contentType ) { xhr.setRequestHeader("Content-Type", s.contentType); }   // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { if ( jQuery.lastModified[s.url] ) { xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]); }   if ( jQuery.etag[s.url] ) { xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]); } }   // Set header so the called script knows that it's an XMLHttpRequest // Only send the header if it's not a remote XHR if ( !remote ) { xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); }   // Set the Accepts header for the server, depending on the dataType xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ? s.accepts[ s.dataType ] + ", */*" : s.accepts._default ); } catch(e) {}   // Allow custom headers/mimetypes and early abort if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) { // Handle the global AJAX counter if ( s.global && ! --jQuery.active ) { jQuery.event.trigger( "ajaxStop" ); }   // close opended socket xhr.abort(); return false; }   if ( s.global ) { trigger("ajaxSend", [xhr, s]); }

这一段代码主要是获得xhr,并在xhrsend之前设置一些header,并调用一些回调函数,比如beforeSend,CallBackcontext是我们在originSettings设置的回调函数的执行上下文。从判断条件可以看出,我们在beforesennd中返回false就会导致此次请求取消。如果当前没有其他请求的话还会触发ajaxstop事件。

var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) { // The request was aborted if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) { // Opera doesn't call onreadystatechange before this point // so we simulate the call if ( !requestDone ) { complete(); }   requestDone = true; if ( xhr ) { xhr.onreadystatechange = jQuery.noop; }   // The transfer is complete and the data is available, or the request timed out } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) { requestDone = true; xhr.onreadystatechange = jQuery.noop;   status = isTimeout === "timeout" ? "timeout" : !jQuery.httpSuccess( xhr ) ? "error" : s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" : "success";   var errMsg;   if ( status === "success" ) { // Watch for, and catch, XML document parse errors try { // process the data (runs the xml through httpData regardless of callback) data = jQuery.httpData( xhr, s.dataType, s ); } catch(err) { status = "parsererror"; errMsg = err; } }   // Make sure that the request was successful or notmodified if ( status === "success" || status === "notmodified" ) { // JSONP handles its own success callback if ( !jsonp ) { success(); } } else { jQuery.handleError(s, xhr, status, errMsg); }   // Fire the complete handlers complete();   if ( isTimeout === "timeout" ) { xhr.abort(); }   // Stop memory leaks if ( s.async ) { xhr = null; } } };

本人觉得onreadystatechange是ajax函数另一个比较重要的地方。

最关心也是当status==‘success’时的处理。下面是jquery工具函数httpData的具体内容

httpData: function( xhr, type, s ) { var ct = xhr.getResponseHeader("content-type") || "", xml = type === "xml" || !type && ct.indexOf("xml") >= 0, data = xml ? xhr.responseXML : xhr.responseText;   if ( xml && data.documentElement.nodeName === "parsererror" ) { jQuery.error( "parsererror" ); }   // Allow a pre-filtering function to sanitize the response // s is checked to keep backwards compatibility if ( s && s.dataFilter ) { data = s.dataFilter( data, type ); }   // The filter can actually parse the response if ( typeof data === "string" ) { // Get the JavaScript object, if JSON is used. if ( type === "json" || !type && ct.indexOf("json") >= 0 ) { data = jQuery.parseJSON( data );   // If the type is "script", eval it in global context } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) { jQuery.globalEval( data ); } }   return data; },

ajax返回的内容只有两种格式,responseText 和responseXML。如果我们设置了datafilter,那么此函数是会在所有对数据的操作之前就行过滤。我们请求格式中json,jsonp,script都是回忆string返回数据。如果是jsonp或是script则先执行data对应的js代码,然后返回数据。(就是客户端脚本注入),如果是json类型,则会先将字符串解析为js对象,然后返回。

// Override the abort handler, if we can (IE doesn't allow it, but that's OK) // Opera doesn't fire onreadystatechange at all on abort try { var oldAbort = xhr.abort; xhr.abort = function() { if ( xhr ) { oldAbort.call( xhr ); }   onreadystatechange( "abort" ); }; } catch(e) { }   // Timeout checker if ( s.async && s.timeout > 0 ) { setTimeout(function() { // Check to see if the request is still happening if ( xhr && !requestDone ) { onreadystatechange( "timeout" ); } }, s.timeout); }   // Send the data try { xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null ); } catch(e) { jQuery.handleError(s, xhr, null, e); // Fire the complete handlers complete(); }   // firefox 1.5 doesn't fire statechange for sync requests if ( !s.async ) { onreadystatechange(); }   function success() { // If a local callback was specified, fire it and pass it the data if ( s.success ) { s.success.call( callbackContext, data, status, xhr ); }   // Fire the global callback if ( s.global ) { trigger( "ajaxSuccess", [xhr, s] ); } }   function complete() { // Process result if ( s.complete ) { s.complete.call( callbackContext, xhr, status); }   // The request was completed if ( s.global ) { trigger( "ajaxComplete", [xhr, s] ); }   // Handle the global AJAX counter if ( s.global && ! --jQuery.active ) { jQuery.event.trigger( "ajaxStop" ); } } function trigger(type, args) { (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args); }   // return XMLHttpRequest to allow aborting the request etc. return xhr;

下面就是一些细节上处理,事件的触发,浏览器的兼容处理,当然还有最重要的一句  xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );

最后函数会返回此xhr请求对象。此外函数success conplete看来应该是我们自己写的回调函数会取代默认,其实success 和compete中还有ajax事件出发的任务,所以我们在settings中写的回调函数只是被ajax的相应函数调用而已。

最后一点,在上面我们讲jsonp请求时为其注册过一个回调函数,你可能会问这到底什么时候调用呢,其实这就要看服务器端相应回来的script中有没有对此函数的调用了。因为我们已经将回调函数名传递给了服务器端了。(这是我的理解,关于jsonp我也是知之甚少)

虽然原本ajax请求结果只有两种responseText和responseXML但是ajax在其上根据具体的协议为我们作了很多处理,虽然我们看不到,但还是应该有所了解。

转载于:https://blog.51cto.com/pangxiezhou/641953

闲着看看jquery.ajax源码相关推荐

  1. 基于java闲一品交易平台计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署

    基于java闲一品交易平台计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 基于java闲一品交易平台计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 本源码技术栈: 项目 ...

  2. 改善ERP的用户体验,个性化用户界面(Jquery 提供源码)

    改善ERP的用户体验,个性化用户界面(Jquery 提供源码) 这篇文章讲述的技术问题并不多,如果你是想了解技术实现,请直接跨过文章下载源码或者看  demo 我大胆起这个名字,有点标题党.希望能对一 ...

  3. JavaScript中Ajax源码

    很多面试会要求介绍Ajax源码,于是这里贴出来以便查阅 function loadXMLDoc() {var xmlhttp;if (window.XMLHttpRequest){// code fo ...

  4. jQuery方法源码解析--jQuery($)方法(一)

    jQuery方法源码解析--jQuery($)方法 注: 1.本文分析的代码为jQuery.1.11.1版本,在官网上下载未压缩版即可 2.转载请注明出处 jQuery方法: 这个方法大家都不陌生,在 ...

  5. asp.net ajax 源码,asp.net+jquery+ajax简单留言板 v1.2

    asp.netC#+jquery1.4.1 +ajax留言板程序说明 采用asp.net C#+ jquery1.4.1 +ajax的实现 主要用aspx文件请求 还可以用ashx处理 ajax返回类 ...

  6. 【深入浅出jQuery】源码浅析--整体架构(转)

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  7. jquery项目源码_第一个jQuery程序

    1.配置jQuery环境 1.1获取jQuery最新版本 进入jQuery的官方网址 http://jquery.com ,下载最新的jQuery库. 1.2 jQuery库类型说明 目前jQuery ...

  8. jQuery Easyui 源码分析之combo组件

    /** * jQuery EasyUI 1.3.1 * 该源码完全由压缩码翻译而来,并非网络上放出的源码,请勿索要.*/ (function($) {function setSize(target, ...

  9. 剖析根据汉字转拼音的JQuery插件源码

    前言 最近需要写一个带右侧索引的通讯录(移动端)页面,既然看到了通讯录,那么自然首先必须的解决汉字转拼音问题(不然不好做分类排序),看网上有一个现成的插件,也就懒得自己写了.这里拿出来给大家聊一聊下他 ...

最新文章

  1. oracle怎么adi导入,Web adi 导入笔记 详细图解
  2. 设计模式复习-状态模式
  3. 数据分析方法论2——流量分析
  4. 自动驾驶科普:一辆无人车到底是怎样工作的?
  5. hadoop loadBalance源码分析
  6. 痛失大家!中国科学院院士陈家镛逝世
  7. CodeVS 3027 线段覆盖2(DP)
  8. mysql bean分页查询_javabean 来实现 MySQL 的分页
  9. 什么是HTML5前端开发?HTML5前端要学哪些技术?
  10. [论文阅读] BCNet: Bidirectional collaboration network for edge-guided salient object detection
  11. Top1方案源码和数据,腾讯广告受众基础属性预估
  12. datetime.strptime格式转换报错ValueError
  13. 告诉你怎么用Python进行企业营运分析!盈利这么多?
  14. C语言之迷宫小游戏2.0版(随机生成地图,可变颜色,优化游戏体验)
  15. 钢铁侠c语言图片,揭秘!钢铁侠马克1型战衣原来使用了这个!
  16. 【炼数成金 NOSQL引航 一 】 进入NoSQL世界;NoSQL与SQL的战争
  17. Conmi的正确答案——米家定时模块的使用以及showOnTimerType、showOffTimerType、showPeriodTimerType、identify的陷阱
  18. python鼠标绘图_python opencv入门 鼠标绘图(4)
  19. Workbench导入xls文件
  20. hpet 定时器中断 8259 linux,[OSDEV]编程高精度定时器(HPET)

热门文章

  1. jQuery通过event获取点击事件的事件对象
  2. 移动管理进步显著 企业仍然面临风险
  3. [leetcode]Symmetric Tree
  4. 博客园使用攻略之如何添加自己的js文件
  5. Javascript事件模型系列(一)事件及事件的三种模型
  6. Javascript: IE中命名函数直接量的Bug?
  7. mysql误删除ibdata1以及日志ib_logfile*
  8. NLog自定义字段写入数据库表,示例
  9. 使用BitmapFactory压缩图片遇到的问题总结
  10. oracle goldengate实施简明介绍