• jQuery源码分析系列:AJAX


     jQuery的实现表示很复杂,但是也能学到很多的东西。

    jQuery确实是一个挺好的轻量级的JS框架,能帮助我们快速的开发JS应用,并在一定程度上改变了我们写JavaScript代码的习惯。

    废话少说,直接进入正题,我们先来看一些简单的方法,这些方法都是对jQuery.ajax()进行封装以方便我们使用的方法,当然,如果要处理复杂的逻辑,还是需要用到jQuery.ajax()的(这个后面会说到).

    1. load( url, [data], [callback] ) :载入远程 HTML 文件代码并插入至 DOM 中。

    url (String) : 请求的HTML页的URL地址。

    data (Map) : (可选参数) 发送至服务器的 key/value 数据。

    callback (Callback) : (可选参数) 请求完成时(不需要是success的)的回调函数。

    这个方法默认使用 GET 方式来传递的,如果[data]参数有传递数据进去,就会自动转换为POST方式的。jQuery 1.2 中,可以指定选择符,来筛选载入的 HTML 文档,DOM 中将仅插入筛选出的 HTML 代码。语法形如 "url #some > selector"。

    这个方法可以很方便的动态加载一些HTML文件,例如表单。

    示例代码:

    $(".ajax.load").load("http://www.cnblogs.com/QLeelulu/archive/2008/03/30/1130270.html .post",
    		function (responseText, textStatus, XMLHttpRequest){
    		this;//在这里this指向的是当前的DOM对象,即$(".ajax.load")[0]	
    		//alert(responseText);//请求返回的内容
    		//alert(textStatus);//请求状态:success,error
    		//alert(XMLHttpRequest);//XMLHttpRequest对象
    });
    这里将显示结果。

    注:不知道为什么URL写绝对路径在FF下会出错,知道的麻烦告诉下。下面的get()和post()示例使用的是绝对路径,所以在FF下你将会出错并不会看到返回结果。还有get()和post()示例都是跨域调用的,发现传上来后没办法获取结果,所以把运行按钮去掉了。

    2. jQuery.get( url, [data], [callback] ):使用GET方式来进行异步请求

    参数:

    url (String) :  发送请求的URL地址.

    data (Map) : (可选) 要发送给服务器的数据,以 Key/value 的键值对形式表示,会做为QueryString附加到请求URL中。

    callback (Function) : (可选) 载入成功时回调函数(只有当Response的返回状态是success才是调用该方法)。

    这是一个简单的 GET 请求功能以取代复杂 $.ajax 。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用 $.ajax。示例代码:

    		$.get("./Ajax.aspx", {Action:"get",Name:"lulu"}, function (data, textStatus){
    				//返回的 data 可以是 xmlDoc, jsonObj, html, text, 等等.
    				this; // 在这里this指向的是Ajax请求的选项配置信息,请参考下图
    				alert(data);
    				//alert(textStatus);//请求状态:success,error等等。
    当然这里捕捉不到error,因为error的时候根本不会运行该回调函数
    //alert(this); });

    点击发送请求:

    jQuery.get()回调函数里面的 this ,指向的是Ajax请求的选项配置信息:

    image

    3. jQuery.post( url, [data], [callback], [type] ) :使用POST方式来进行异步请求

    参数:

    url (String) : 发送请求的URL地址.

    data (Map) : (可选) 要发送给服务器的数据,以 Key/value 的键值对形式表示。

    callback (Function) : (可选) 载入成功时回调函数(只有当Response的返回状态是success才是调用该方法)。

    type (String) : (可选)官方的说明是:Type of data to be sent。其实应该为客户端请求的类型(JSON,XML,等等)

    这是一个简单的 POST 请求功能以取代复杂 $.ajax 。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用 $.ajax。示例代码:

    Ajax.aspx:

    Response.ContentType = "application/json";
    Response.Write("{result: '" + Request["Name"] + ",你好!(这消息来自服务器)'}");

    jQuery 代码:

    $.post("Ajax.aspx", { Action: "post", Name: "lulu" },
    		function (data, textStatus){
    			// data 可以是 xmlDoc, jsonObj, html, text, 等等.
    			//this; // 这个Ajax请求的选项配置信息,请参考jQuery.get()说到的this
    			alert(data.result);
    		}, "json");

    点击提交:

    这里设置了请求的格式为"json":

    image

    如果你设置了请求的格式为"json",此时你没有设置Response回来的ContentType 为:Response.ContentType = "application/json"; 那么你将无法捕捉到返回的数据。

    注意一下,alert(data.result); 由于设置了Accept报头为“json”,这里返回的data就是一个对象,并不需要用eval()来转换为对象。

    4. jQuery.getScript( url, [callback] ) : 通过 GET 方式请求载入并执行一个 JavaScript 文件

    参数

    url (String) : 待载入 JS 文件地址。

    callback (Function) : (可选) 成功载入后回调函数。

    jQuery 1.2 版本之前,getScript 只能调用同域 JS 文件。 1.2中,您可以跨域调用 JavaScript 文件。注意:Safari 2 或更早的版本不能在全局作用域中同步执行脚本。如果通过 getScript 加入脚本,请加入延时函数。

    这个方法可以用在例如当只有编辑器focus()的时候才去加载编辑器需要的JS文件.下面看一些示例代码:

    加载并执行 test.js。

    jQuery 代码:

    $.getScript("test.js");


    加载并执行 AjaxEvent.js ,成功后显示信息。

    jQuery 代码:

    $.getScript("AjaxEvent.js", function(){
    		alert("AjaxEvent.js 加载完成并执行完成.你再点击上面的Get或Post按钮看看有什么不同?");
    });

    加载完后请重新点击一下上面的 Load 请求看看有什么不同。

    jQuery Ajax 事件

    Ajax请求会产生若干不同的事件,我们可以订阅这些事件并在其中处理我们的逻辑。在jQuery这里有两种Ajax事件:局部事件 和 全局事件。

    局部事件就是在每次的Ajax请求时在方法内定义的,例如:

     $.ajax({
       beforeSend: function(){
         // Handle the beforeSend event
       },
       complete: function(){
         // Handle the complete event
       }
       // ...
     });

    全局事件是每次的Ajax请求都会触发的,它会向DOM中的所有元素广播,在上面 getScript() 示例中加载的脚本就是全局Ajax事件。全局事件可以如下定义:

     $("#loading").bind("ajaxSend", function(){
       $(this).show();
     }).bind("ajaxComplete", function(){
       $(this).hide();
     });

    或者:

     $("#loading").ajaxStart(function(){
       $(this).show();
     }); 

    我们可以在特定的请求将全局事件禁用,只要设置下 global 选项就可以了:

     $.ajax({
       url: "test.html",
       global: false,// 禁用全局Ajax事件.
       // ...
     });

    下面是jQuery官方给出的完整的Ajax事件列表:

    • ajaxStart (Global Event)
      This event is broadcast if an Ajax request is started and no other Ajax requests are currently running.
      • 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.)
      • 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).
      • 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.

      具体的全局事件请参考API文档。
      好了,下面开始说jQuery里面功能最强的Ajax请求方法 $.ajax();  

       

      jQuery.ajax( options ) : 通过 HTTP 请求加载远程数据

      这个是jQuery 的底层 AJAX 实现。简单易用的高层实现见 $.get, $.post 等。

      $.ajax() 返回其创建的 XMLHttpRequest 对象。大多数情况下你无需直接操作该对象,但特殊情况下可用于手动终止请求。

      注意: 如果你指定了 dataType 选项,请确保服务器返回正确的 MIME 信息,(如 xml 返回 "text/xml")。错误的 MIME 类型可能导致不可预知的错误。见 Specifying the Data Type for AJAX Requests
      当设置 datatype 类型为 'script' 的时候,所有的远程(不在同一个域中)POST请求都回转换为GET方式。

      $.ajax() 只有一个参数:参数 key/value 对象,包含各配置及回调函数信息。详细参数选项见下。

      jQuery 1.2 中,您可以跨域加载 JSON 数据,使用时需将数据类型设置为 JSONP。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。数据类型设置为 "jsonp" 时,jQuery 将自动调用回调函数。(这个我不是很懂)

      参数列表:

      参数名 类型 描述
      url String (默认: 当前页地址) 发送请求的地址。
      type String (默认: "GET") 请求方式 ("POST" 或 "GET"), 默认为 "GET"。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
      timeout Number 设置请求超时时间(毫秒)。此设置将覆盖全局设置。
      async Boolean (默认: true) 默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
      beforeSend Function 发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。XMLHttpRequest 对象是唯一的参数。
      function (XMLHttpRequest) {
        this; // the options for this ajax request
      }
      cache Boolean (默认: true) jQuery 1.2 新功能,设置为 false 将不会从浏览器缓存中加载请求信息。
      complete Function 请求完成后回调函数 (请求成功或失败时均调用)。参数: XMLHttpRequest 对象,成功信息字符串。
      function (XMLHttpRequest, textStatus) {
        this; // the options for this ajax request
      }
      contentType String (默认: "application/x-www-form-urlencoded") 发送信息至服务器时内容编码类型。默认值适合大多数应用场合。
      data Object,
      String
      发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:["bar1", "bar2"]} 转换为 '&foo=bar1&foo=bar2'。
      dataType String

      预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息返回 responseXML 或 responseText,并作为回调函数参数传递,可用值:

      "xml": 返回 XML 文档,可用 jQuery 处理。

      "html": 返回纯文本 HTML 信息;包含 script 元素。

      "script": 返回纯文本 JavaScript 代码。不会自动缓存结果。

      "json": 返回 JSON 数据 。

      "jsonp": JSONP 格式。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。

      error Function (默认: 自动判断 (xml 或 html)) 请求失败时将调用此方法。这个方法有三个参数:XMLHttpRequest 对象,错误信息,(可能)捕获的错误对象。
      function (XMLHttpRequest, textStatus, errorThrown) {
        // 通常情况下textStatus和errorThown只有其中一个有值 
        this; // the options for this ajax request
      }
      global Boolean (默认: true) 是否触发全局 AJAX 事件。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 。可用于控制不同的Ajax事件
      ifModified Boolean (默认: false) 仅在服务器数据改变时获取新数据。使用 HTTP 包 Last-Modified 头信息判断。
      processData Boolean (默认: true) 默认情况下,发送的数据将被转换为对象(技术上讲并非字符串) 以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
      success Function 请求成功后回调函数。这个方法有两个参数:服务器返回数据,返回状态
      function (data, textStatus) {
        // data could be xmlDoc, jsonObj, html, text, etc...
        this; // the options for this ajax request
      }

      这里有几个Ajax事件参数:beforeSend success complete ,error 。我们可以定义这些事件来很好的处理我们的每一次的Ajax请求。注意一下,这些Ajax事件里面的 this 都是指向Ajax请求的选项信息的(请参考说 get() 方法时的this的图片)。
      请认真阅读上面的参数列表,如果你要用jQuery来进行Ajax开发,那么这些参数你都必需熟知的。

      示例代码,获取博客园首页的文章题目:

      $.ajax({
      		type: "get",
      		url: "http://www.cnblogs.com/rss",
      		beforeSend: function(XMLHttpRequest){
      			//ShowLoading();
      		},
      		success: function(data, textStatus){
      			$(".ajax.ajaxResult").html("");
      			$("item",data).each(function(i, domEle){
      				$(".ajax.ajaxResult").append("<li>"+$(domEle).children("title").text()+"</li>");
      			});
      		},
      		complete: function(XMLHttpRequest, textStatus){
      			//HideLoading();
      		},
      		error: function(){
      			//请求出错处理
      		}
      });

      这里将显示首页文章列表。

       

      其他

      jQuery.ajaxSetup( options ) : 设置全局 AJAX 默认选项。

      设置 AJAX 请求默认地址为 "/xmlhttp/",禁止触发全局 AJAX 事件,用 POST 代替默认 GET 方法。其后的 AJAX 请求不再设置任何选项参数。

      jQuery 代码:

      $.ajaxSetup({
        url: "/xmlhttp/",
        global: false,
        type: "POST"
      });
      $.ajax({ data: myData });

      serialize() 与 serializeArray()

      serialize() : 序列表表格内容为字符串。

      serializeArray() : 序列化表格元素 (类似 '.serialize()' 方法) 返回 JSON 数据结构数据。

      示例:

      HTML代码:

      <p id="results"><b>Results: </b> </p>
      <form>
        <select name="single">
          <option>Single</option>
          <option>Single2</option>
        </select>
        <select name="multiple" multiple="multiple">
          <option selected="selected">Multiple</option>
          <option>Multiple2</option>
          <option selected="selected">Multiple3</option>
        </select><br/>
        <input type="checkbox" name="check" value="check1"/> check1
        <input type="checkbox" name="check" value="check2" 
      checked="checked"/> check2
        <input type="radio" name="radio" value="radio1" 
      checked="checked"/> radio1
        <input type="radio" name="radio" value="radio2"/> radio2
      </form> 

      image

      serializeArray() 结果为:

      image

      一些资源

      一个jQuery的Ajax Form表单插件:http://www.malsup.com/jquery/form/

      一个专门生成Loading图片的站点:http://ajaxload.info/   大家觉得那些Loading比较炫的可以在这里跟帖晒一下,方便大家取用,嘎嘎

    对于ajax的请求,可以分成如下的几步:

    1、通过 new XMLHttpRequest 或其它的形式(指IE)生成ajax的对象xhr。

    2、通 过xhr.open(type, url, async, username, password)的形式建立一个连接。

    3、通过setRequestHeader设定xhr的请求头部(request header)。

    4、通过send(data)请求服务器端的数据。

    5、执行在xhr上注册 的onreadystatechange回调处理返回数据。

    一、生成xhr的时候,根据浏览器类型的不同,使用不同的方式创建:

    源码:

    //创建XMLHttpRequest对象
    function createStandardXHR() {
        try {
            return new window.XMLHttpRequest();
        } catch( e ) {}
    }
    //在IE6,7下的创建XMLHttpRequest
    function createActiveXHR() {
        try {
            return new window.ActiveXObject( "Microsoft.XMLHTTP" );
        } catch( e ) {}
    }
    
    //创建请求对象
    jQuery.ajaxSettings.xhr = window.ActiveXObject ?
    
        function() {
            return !this.isLocal && createStandardXHR() || createActiveXHR();
        } :
        createStandardXHR;//对于除IE外的浏览器

    二、Ajax方法的参数准备:


            AJAX参数详细列表:

            1>>AJAX请求设置。所有选项都是可选的
            2>>async(Boolean):默认异步请求,如果同步 设置为false  用户其他操作在请求完成后才能执行
            3>>beforeSend(function):发送请求前可以修改XMLHttpRequest唯一参数对象,
            4>>complete(function):请求完成后的回调函数  参数:XMLHttpRequest对象和一个描述成功请求类型的字符串
            5>>contentType(string):发送信息到服务器的内容编码类型
            6>>data(object,string):发送到服务器的数据,将自动装换成请求字符串  GET请求中负载URL后 为数组 自动为值对应同一名称
            7>>dataFilter(function):给Ajax返回的原始数据进行预处理的 函数,提供data和type两个参数,data是ajax返回的原始数据 type是dataType
            8>>dataType(string):预期服务器返回的数据类型 如果不确定 将返回responseXML或responseText
            9>>responseText:xml html script json jsonp text
            10>>error(function):失败回调函数 XMLHttpRequest对象  ,错误信息  捕获的错误对象
            11>>global(boolean):是否全局
            12>>ifModified(boolean):仅在服务器数据改变时获取新数据 HTTP  Last-Modified

        //$.ajaxSetup({}) 设置自己的ajax参数,如果target不存在  把$.ajaxSettings默认的参数扩展到target
        //最后都是通过settings参数扩展target参数
        ajaxSetup: function ( target, settings ) {
            //如果只有一个参数
            if ( !settings ) {
                settings = target;
                //将target的值赋给settings,来扩展ajaxSettings,然后将ajaxSettings赋值给target
                target = jQuery.extend( true, jQuery.ajaxSettings, settings );
            } else {
                //用ajaxSettings和settings扩展traget
                jQuery.extend( true, target, jQuery.ajaxSettings, settings );//扩展
            }
            //这个for怎么理解?
            for( var field in { context: 1, url: 1 } ) {
                //字段在settings中
                if ( field in settings ) {
                    target[ field ] = settings[ field ];
                //字段在jQuery.ajaxSettings中
                } else if( field in jQuery.ajaxSettings ) {
                    target[ field ] = jQuery.ajaxSettings[ field ];
                }
            }
            return target;
        },
    
        //ajax默认设置,可以通过$.ajaxSetup进行设置
        ajaxSettings: {
            url: ajaxLocation,//请求的URL
            isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),//本地文件
            global: true,//是否触发全局ajax事件,设置为false将不会触发ajax事件如:ajaxStart 或sjaxStop
            type: "GET",
            contentType: "application/x-www-form-urlencoded",//form形式
            processData: true,//在默认情况下,发送的数据将被转换为对象以配合默认内容类型。如果要发送DOM树信息或其他不希望转换的信息 请设置false
            async: true,//是否异步 注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
            /*
            timeout: 0,//设置请求超时时间
            data: null,//传递的数据
            dataType: null, //html, json, jsonp, script, or text.
            username: null,
            password: null,
            cache: null,//是否缓存文件
            traditional: false,
            headers: {},
            */
            //接受文件头信息
            accepts: {
                xml: "application/xml, text/xml",
                html: "text/html",
                text: "text/plain",
                json: "application/json, text/javascript",
                "*": "*/*"
            },
            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            },
            //返回文件类型
            responseFields: {
                xml: "responseXML",
                text: "responseText"
            },
            //可以进行类型装换的  用于ajax的类型转换器
            converters: {
                // Convert anything to text
                "* text": window.String,
                // Text to html (true = no transformation)
                "text html": true,
                // 文本转换成json
                "text json": jQuery.parseJSON,
                // 文本转换成XML
                "text xml": jQuery.parseXML
            }
        },

    接下来就是实现Ajax的主要方法了,下面是源码的分析:

       1、通过 new XMLHttpRequest 或其它的形式(指IE)生成ajax的对象xhr。
            2、通 过xhr.open(type, url, async, username, password)的形式建立一个连接。
            3、通过setRequestHeader设定xhr的请求头部(request header)。
            4、通过send(data)请求服务器端的数据。
            5、执行在xhr上注册 的onreadystatechange回调处理返回数据。

    这个真心有点长啊!

    Main method
        ajax: function( url, options ) {    //$.ajax({url:'xx',data:{},});
            //如果url是对象的话  修正参数
            if(typeof url == "object"){
                options = url;
                options = undefined;
            }
            //确保options是对象,不会产生undefined
            options = options || {};
    
            //创建ajax的很多私有变量,    可以通过ajaxSetup改变
            var s = jQuery.ajaxSetup({},options);//创建最终的ajax参数对象s
                //回调函数的上下文,如果有context就是s.context,否则是s    s.context是什么?        传进去的上下文属性
                callbackContext = s.context || s;
    
                //事件作用域:  如果是DOM node 或者 jQuery collection    ??
                //回调函数作用域不是s    DOM节点或回调函数作用域是jQuery的实例        ?     jQuery的回调函数上下文     :    事件
                globalEventContext = callbackContext !== s &&
                (callbackContext.nodeType || callbackContext instanceof jQuery) ? jQuery(callbackContext) :    jQuery.event,
    
                //异步队列:
                deferred = jQuery.Deferred(),
                completeDeferred = jQuery._Deferred(),
    
                //一组数值的HTTP代码和函数对象  $.ajax({statusCode:{404:function(){alert('page no found');};}});        默认为空对象
                statusCode = s.statusCode || {},
                ifModifiedKey,
    
                //请求头部
                requestHeaders = {},
                requestHeadersNames = {},
    
                //响应headers
                responseHeadersString,//响应头部字符串
                responseHeaders,//响应头部
    
                transport,
    
                //请求超时时间,异步请求下请求多少时间后终止请求
                timeoutTimer,
    
                //判断是否是跨域请求的变量
                parts,
    
                //jqXHR state
                state =0,
                //是否全局事件
                fireGlobals,
                //loop variable
                i,
    
                jqXHR =    {
    
                    /*
                    readyState五种状态:  onreadystatechange回调函数
                        0>>uninitialized:未初始化 未调用open()
                        1>>Loading:已调用open() 未调用send()方法正在发送请求
                        2>>loaded:已经调用send()当尚未收到响应
                        3>>interactive:正在解析响应内容,已经接收到部分响应内容
                        4>>completed:完成 可以在客户端调用了
                    */
    
                    readyState:0,//初始状态
    
                    //设置dataType,达到预期服务器返回的数据类型 jQuery将自动根据HTTP的MIME包信息判断    name:头部字段名称 value:头部字段值
                    //设置请求头部,示例:xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
                    setRequestHeader:function(name,value){
                        //这里state = 0,发送请求前,设置请求头
                        if(!state){
                            var lname = name.toLowerCase();
                            name = requestHeadersNames[lname] = requestHeadersNames[lname] || name;
                            requestHeaders[name] = value;//私有requestHeadersNames
                        }
                        return this;
                    },
    
                    /*
                        原生字符串,取的是包含所有头部信息的长字符串
                        getAllResponse-Headers():
                        返回:Date:        ,
                             server:Appache/1.3.39(Unix),
                             Vary:Accept,
                             X-Powered-By:PHP,
                             Connection:close,
                             Content-Type:text/html;charset =utf-8;
                    */
                    getAllResponseHeaders:function(){
                        //state === 2,已经调用send()当尚未收到响应
                        return state === 2 ? responseHeadersString : null;
                    },
    
                    //传入头部字段名称,取得相应的响应头部信息
                    getResponseHeader:function(key){
                        var match;
                        if(state ===2){
                            if(!responseHeaders){
                                responseHeaders = {};
                                while ((match = rheaders.exec(responseHeadersString))){
                                    responseHeaders[match[1].toLowerCase()] = match[2];
                                }
                            }
                            //获得Headers内容value
                            match = responseHeaders[key.toLowerCase()];
                        }
                        return match === undefined ? null : match;
                    },
                    //overrides(重写) response context-type header   mimeType
                    /*用法 在beforeSend中可以用
                    $.ajax({
                          url: "http://fiddle.jshell.net/favicon.png",
                          beforeSend: function ( xhr ) {
                            xhr.overrideMimeType("text/plain; charset=x-user-defined");
                          }
                    }).done(function ( data ) {
                          if( console && console.log ) {
                            console.log("Sample of data:", data.slice(0, 100));
                          }
                    });
                    */
                    //将资源的媒体类型  设置成传递进来的
                    overrideMimeType:function(type){//重写MimeType
                        if(!state){
                            s.mimeType = type;//数据类型 将资源的媒体类型
                        }
                        return this;
                    },
                    // cancel the request 中断请求
                    abort:function(statusText){
                        statusText = statusText || "abort";
                        if(tarnsport){
                            transport.abort(statusText);//xhr.abort();不在允许访问任何响应有关的属性
                        }
                        done(0,statusText);//调用下面的
                        return this;
                    }
                };
            //callback for when everything is done
            /*
                2:request received          ---->readyState
                4:request finished and response is ready
                200:"OK"
                404:page not found          --->status
    
                1.清除本次请求用到的变量
                2.解析状态码&状态描述
                3.执行异步回调函数队列
                4.执行complete队列
                5.触发全局ajax
    
                status -1 没有找到请求分发器
                闭包函数done  在done中判断本次请求是否成功,如果成功就调用ajaxConvert对响应的数据进行类型转换
            */
            /************************************    done()处理不同状态的request        **************************************/
            function done( status, statusText, responses, headers ) {//处理不同状态的request
                //表示已经请求过一次,立即返回,第一次请求默认的state=0
                if ( state === 2 ) {
                    return;
                }
                //正在请求
                //state is "done" now
                state = 2;//因为XMLHttpRequest的 2 是request received
    
                //如果存在超时请求,清除这个超时时间
                if ( timeoutTimer ) {
                    clearTimeout( timeoutTimer );
                }
    
                transport = undefined;//中止传输 为了垃圾回收
                // Cache response headers 缓存请求头部
                responseHeadersString = headers || "";//响应头部字符串 headers:函数传递进来的
    
                // Set readyState
                jqXHR.readyState = status ? 4 : 0;
    
                var isSuccess,
                    success,
                    error,
                    //ajaxHandleResponses()是做什么的?
                    response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
                    lastModified,
                    etag;
                //status:响应的HTTP状态  200:成功标志  304 请求资源并没有修改,可以读缓存中的数据
                //statusText:HTTP状态说明
                //responseText:对于非XML,内容保存在responseText中        responseXML为空
    
                //如果成功 处理类型        状态是200
                if ( status >= 200 && status < 300 || status === 304 ) {
                    //检查服务器端的文件是否被改变    改变了处理返回的头部信息,获取必要的信息即可
                    if ( s.ifModified ) {
                        if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
                            jQuery.lastModified[ ifModifiedKey ] = lastModified;
                        }
                        if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
                            jQuery.etag[ ifModifiedKey ] = etag;
                        }
                    }
                    //服务器内容没有发生改变,修改状态数据,设置成功
                    if ( status === 304 ) {
                        statusText = "notmodified";//空的响应体 和   304
                        isSuccess = true;
                    } else {// If we have data
                        try {
                            //----------------------------------------------------------------------------------
                            //------------------------ 类型转换器:转换请求的数据类型,(XML,json,js) ------------------
                            //----------------------------------------------------------------------------------
                            //类型转换器,转换成指定的文件类型    将server返回的数据进行相应的转化(js json等)
                            success = ajaxConvert( s, response );//这里的sucess变为转换后的数据对象
                            statusText = "success";
                            isSuccess = true;//200状态 isSucess = true
                        } catch(e) {
                            //数据类型转换器解析时出错
                            statusText = "parsererror";
                            error = e;
                        }
                    }
                //非200-300,也非304
                } else {
                    //从statusText中提取error  标准化 statusText和status 其他的异常状态 格式化statusText status,不采用标准的HTTP码
                    error = statusText;
                    if( !statusText || status ) {
                        statusText = "error";
                        if ( status < 0 ) {
                            status = 0;
                        }
                    }
                }
    
                //把状态参数和状态内容赋给jqXHR
                jqXHR.status = status;//设置jqXHR.status 其实也就是XMLHttpRequest状态
                jqXHR.statusText = statusText;
    
                //获取成功与失败的回调函数
                if ( isSuccess ) {
                    //回调上下文和参数
                    deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
                } else {
                    deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
                }
    
                //$.ajax({statusCode:{404:function(){alert('page no found');};}});
                //获得当前的状态码回调函数,函数在下面
                jqXHR.statusCode( statusCode );//状态参数代码  默认为空
                statusCode = undefined;
    
                //是否全局事件        ,$(selector).trigger()    触发被选元素的指定事件类型
                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
                            [ jqXHR, s, isSuccess ? success : error ] );
                }
                //complete() 函数。    resolveWith
                completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
    
                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
                    //全局ajax计数器
                    if ( !( --jQuery.active ) ) {
                        jQuery.event.trigger( "ajaxStop" );
                    }
                }
            }
            /************************************    End done()        **************************************/
    
            //给jqXHR对象赋上deferred对象的只读方法,包括done,alwaysis,Resolved
            deferred.promise( jqXHR );
            jqXHR.success = jqXHR.done;
            jqXHR.error = jqXHR.fail;
            jqXHR.complete = completeDeferred.done;
    
            //获取当前的status状态码的回调函数
            jqXHR.statusCode = function( map ) {//map={200:"..",0:".."}
                if ( map ) {
                    var tmp;//临时变量
                    if ( state < 2 ) {//请求还没回调
                        for( tmp in map ) {
                            statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];//键值对
                        }
                    } else {//请求完成阶段    ,jQuery返回相应的callback,then 调用
                        tmp = map[ jqXHR.status ];
                        jqXHR.then( tmp, tmp );//我们的状态代码在这里调用,通过jqXHR.then方法调用
                    }
                }
                return this;
            };
    
            //  Remove 将url的hash去掉 rhash=/#.*$/ rprotocol = /^\/\//  ajaxLocParts[1]="http:"        ajaxLocParts[ 1 ] + "//"也就是http://
            s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
            //取出dataType,不存在为 *     去空格,然后以空格拆分数据类型    rspacesAjax = /\s+/ 如:s.dataType=[*]
            s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
    
    
            //设置crossDomain ,同域为false,跨域为true 如果想强制跨域请求(如JSONP形式)同一域,设置crossDomain为true
            if ( s.crossDomain == null ) {
                //同ajaxLocParts变量,这里也是通过这个请求的url来判断是否是跨域请求。rurl是将url拆分
                parts = rurl.exec( s.url.toLowerCase() );
    
                //这里来判断是否是跨域请求,又是!! 主要判断请求的url的parts与 ajaxLocParts
                s.crossDomain = !!( parts &&
                    ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
                        ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                            ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
                );
            }
    
            //生成加到字符串后面的参数字符串 ?号后面的      序列化传递的参数  processData:请求进度
            //如果存在data参数并且 processData=true&&s.data为一个对象
            if ( s.data && s.processData && typeof s.data !== "string" ) {
                s.data = jQuery.param( s.data, s.traditional );//序列化参数
            }
    
            //----------------------------------------------------------------------------------
            //------------------发送请求前,调用前置过滤器--------------------------------------------
            //----------------------------------------------------------------------------------
            inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
    
            //前置过滤器中中止了 停止请求
            if ( state === 2 ) {
                return false;
            }
            //判断是否触发全局事件
            fireGlobals = s.global;
    
            //将状态码改为大写    请求的方式
            s.type = s.type.toUpperCase();
            //请求方式是否变化
            s.hasContent = !rnoContent.test( s.type );//状态码是否有内容,是否变化 没变化说明没有修改
    
            //一个新的请求 ajaxStart    开始
            if ( fireGlobals && jQuery.active++ === 0 ) {
                jQuery.event.trigger( "ajaxStart" );
            }
            // More options handling for requests with no content
            if ( !s.hasContent ) {
                if ( s.data ) {                //这就是为什么data参数会被传到url    ?号    后面
                    //判断是否有问号,有酒用 &链接参数 没有就用 ?
                    s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
                }
                // Get ifModifiedKey before adding the anti-cache parameter
                ifModifiedKey = s.url;//获得ifModifiedKey  读取最后修改时间判定
                //没有缓存内容        s.cache是否存在
                if ( s.cache === false ) {
                    var ts = jQuery.now(),
                        //用当前时间替换掉原来的最后修改时间
                        ret = s.url.replace( rts, "$1_=" + ts );
                    //如果没变,将时间戳加到后面
                    s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
                }
            }
    
            //设置header
            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
                jqXHR.setRequestHeader( "Content-Type", s.contentType );
            }
            //请求的内容是否改变
            if ( s.ifModified ) {
                //请求的关键字就是 :请求的url
                ifModifiedKey = ifModifiedKey || s.url;
                //http://www.cnblogs.com/czh-liyu/archive/2011/06/22/2087113.html
                if ( jQuery.lastModified[ ifModifiedKey ] ) {
                    //最后修改时间
                    jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
                }
                if ( jQuery.etag[ ifModifiedKey ] ) {
                    //任意属性
                    jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
                }
            }
            /*
                设置dataType,达到预期服务器返回的数据类型,如果没有dataType参数,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断
                   默认的        s.accepts=  accepts:{    xml: "application/xml, text/xml",
                                                        html: "text/html",text: "text/plain",
                                                        json: "application/json, text/javascript",
                                                        "*": allTypes
                                                }
    
                   allTypes= ['*\/']+['*']  如果设置了dataType参数,即 s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().split(/\s+/);
                   比如:$(url,{dataType:"json"}) 这里第二个参数将成为 "application/json, text/javascript"+",*\/*;q=0.01"
             */
            //Set the Accepts header for the server, depending on the dataType
            //浏览器能够处理的内容类型
            jqXHR.setRequestHeader(
                "Accept",
                s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
                    s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
                    s.accepts[ "*" ]
            );
    
            //检查头部是否设置了参数 s.headers={} 一个额外的"{键:值}"对映射到请求一起发送   -->没设置头部
            for ( i in s.headers ) {
                jqXHR.setRequestHeader( i, s.headers[ i ] );
            }
    
            //发送请求前的拦截函数,确保满足条件
            if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
                    jqXHR.abort();//中止请求  禁止访问属性
                    return false;
            }
            //$.ajax().success(callback).error(callback).complete(callback)在这里增加callback 这里表面现在ajax请求可以不止一个回调函数
            for ( i in { success: 1, error: 1, complete: 1 } ) {
                jqXHR[ i ]( s[ i ] );
            }
    
            //----------------------------------------------------------------------------------
            //------------------------------请求分发器--------------------------------------------
            //----------------------------------------------------------------------------------
            transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
            //如果没有请求分发器自动终止
            if ( !transport ) {
                done( -1, "No Transport" );
            } else {
                jqXHR.readyState = 1;
                // Send global event 发送全局的事件
                if ( fireGlobals ) {
                    globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
                }
                //Timeout设置请求超时时间(毫秒),超时请求后,jqXHR.abort() 中止请求,简单的意思就是在指定的时间内还未响应,停止请求
                if ( s.async && s.timeout > 0 ) {
                    timeoutTimer = setTimeout( function(){
                        jqXHR.abort( "timeout" );
                    }, s.timeout );
                }
                try {
                    state = 1;
                    //发送请求头部  调用transport.send()方法
                    transport.send( requestHeaders, done );
                } catch (e) {
                    // Propagate exception as error if not done
                    if ( status < 2 ) {
                        done( -1, e );
                    } else {
                        jQuery.error( e );
                    }
                }
            }
            return jqXHR;
        },
        //序列化参数
        param: function( a, traditional ) {
            var s = [],//空数组
                add = function( key, value ) {//声明方法
                    value = jQuery.isFunction( value ) ? value() : value;
                    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
                };
    
            if ( traditional === undefined ) {//没设定就调用默认的
                traditional = jQuery.ajaxSettings.traditional;
            }
            //数组
            if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
                jQuery.each( a, function() {
                    //遍历a中的name 和 value
                    add( this.name, this.value );//调用上面的方法
                });
            //对象
            /*else{旧的实现
                for (var j in a){
                    //value是数组,key 要重复
                    if (a[j] && a[j].constructor == Array){
                        jQuery.each(a[j],function(){
                            add(j,this);
                        }
                    }
                }
            }*/
            } else {
                for ( var prefix in a ) {
                    buildParams( prefix, a[ prefix ], traditional, add );
                }
            }
            return s.join( "&" ).replace( r20, "+" );
        }
    });

    这中间用到了异步队列调用和前置过滤器、分发器以及类型转换器。异步队列独立在前面的文章中了。

    前置过滤器:


        *         undefined     不做任何处理 事实上也没有 * 属性
        json    function     被当做 * 处理
        jsonp    function     修正url或data 增加回调函数 在window上注册回调函数  注册script > json数据转换器
        scirpt    fucntion     设置以下参数:是否缓存 cache  如果跨域 请求类型  如果跨域 是否触发AJAX全局事件

        调用jQuery.ajaxPrefilter填充prefilters  过滤器
        Detect, normalize options and install callbacks for jsonp requests
        向前置过滤器对象中添加特定类型的过滤器
        添加过滤器将格式化参数 并且为jsonp请求增加callbacks
        MARK:AJAX模块初始化

     源码分析:

    ajaxPrefilter
    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
        //如果是表单提交,检查数据是字符串
        var inspectData = s.contentType === "application/x-www-form-urlencoded" && (typeof s.data === "string");
        //这个方法只处理jsonp,如果json的url或data有jsonp的特征被当成jsonp处理
        //触发jsonp的3种方式
        if(s.dataTypes[0] === "jsonp" ||//如果是jsonp
            s.jsonp !== false && (jsre.test(s.url) ||//为禁止jsopn, s.url中包含=?& =?$ ??
            inspectData && jsre.test(s.data))){//s.data中包含=?& =?$ ??
                var responseContainer,
                    //如果s.jsonpCallback是函数 执行s.jsonpCallback作为函数名的回调函数
                    //回调函数
                    jsonpCallback = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ? s.jsonpCallback() : s.jsonpCallback,
                    previous = window[jsonpCallback],//window上的函数
                    url = s.url,
                    data = s.data,
                    //jsre =       ;//=?& =?$ ??
                    repalce = "$1" + jsonpCallback + "$2";//$1=,$2 &|$ 加入时间戳
                    if(s.jsopn !== false){
                        url = url.replace(jsre,replace);//将回调函数名插入url
                        if(s.url === url){//如果url没有变化,尝试修改data
                            if(inspectData){//数据类型
                                data = data.replace(jsre,replace);//将回调函数插入data
                            }
                            if(s.data === data){//如果data也咩有变化
                                url += (/\?/.test(url) ? "&" : "?") +s.jsonp + "=" + jsonpCallback;  //自动在url后面添加回调函数
                            }
                        }
                    }
                    //存储可能改变过的url  data
                    /* 修正url 和 data */
                    s.url = url;
                    s.data = data;
                    //install callback
                    /* 在window上注册回调函数 */
                    window[jsonpCallback] = function(response){//在window上注册回调函数
                        responseContainer = [response];
                    };
    
                    //clean-up function
                    jqXHR.always(funciton(){
                        //将备份的previous函数恢复
                        window[jsonpCallback] = previous;
                        //响应完成时调用的jsonp回调函数
                        if(responseContainer && jQuery.isFunction(previous)){
                            window[jsonpCallback](responseContainer[0]);
                        }
                    });
                    s.converters["script json"] = function(){
                        if(!responseContainer){
                            jQUery.error(jsonpCallback + "was not called");
                        }
                        return responseConteainer[0];//作为方法的参数传入,本身就是一个json对象不需要再做转换
                    };
                    /* 注册scirpt>json 数据转换器 */
                    s.dataTypes[0] = "json";//强制为json
                    return "script";//jsonp > json
            }
    });
    
    //设置script的前置过滤器,script并不一定跨域
    //设置以下参数:是否缓存 cache  (如果跨域) 请求类型  (如果跨域) 是否触发AJAX全局事件
    jQuery.ajaxPrefilter( "script", function( s ) {
        if(s.cache == undefined){//如果缓存为设置 定义为false
            s.cache = false;
        }
        if(s.crossDomain){//如果跨域未被禁用 GET  不触发全局事件
            s.type = "GET";
            s.global = false;
        }
    });

    请求分发器:

     

        *        function:返回xhr分发器 分发器带有send,abort方法  send方法依次调用open send方法发送请求并绑定onreadystatechange事件句柄
        script    function:返回script分发器 担忧send  abort方法   send方法通过在header中创建scirpt标签异步载入js
                        并在scirpt元素上绑定onload onreadystatechange事件句柄

        jQuery.ajaxTransport填充taransports 分发器
        Bind script tag hack transport            open() 和 send()方法

     

    源码分析:

    ajaxTransport
    jQuery.ajaxTransport( "script", function(s) {
        if(s.crossDomain){//script 可能是json 或 jsonp  jsonp需要跨域
            //如果在本域中设置了跨域会怎么处理 ?
            var script,
                head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;//利用布尔表达式计算顺序
            return {
                /*以下即是异步加载js的常用方法*/
                send:function(_,callback){//提供与同域请求一致的接口
                    script = document.createElement("script");//通过创script标签来实现
                    script.async = "async";
                }
                if(s.scirptCharset){
                    scirpt.charset = s.scriptCharset;//字符集
                }
                script.src = s.url;//动态载入
                // script加载完成触发的callback
                script.onload = scirpt.onreadystatechange = function(_,isAbort){//js文件加载后的callback
                        if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
    
                            //处理ie下得内存泄露问题 ,onload事件触发后,销毁事件.
                            script.onload = script.onreadystatechange = null;
    
                            if ( head && script.parentNode ) {
                                //加载过后删除script标签元素
                                head.removeChild( script );
                            }
                            //注销script
                            script = undefined;
                            if ( !isAbort ) {
                                callback( 200, "success" );//执行回调函数  200为HTTP状态码
                            }
                        }
                    };
    
                    //把创建的script加入head头部,但是加载完成后会被清除该script标签,用insertBefore代替appendChild,如果IE6  有bug
                    head.insertBefore( script, head.firstChild );
                },
                abort: function() {
                    if ( script ) {
                        //手动触发onload时间 jqXHR状态码为0  HTTP状态码为1XX
                        script.onload( 0, 1 );
                    }
                }
            };
        }
    });

    addToPrefiltersOrTransports():
        首先,prefilters和transports被置为空对象:添加全局前置过滤器或请求分发器,过滤器在发送之前调用,    分发器用于区分ajax请求和script标签请求
        prefilters = {}, // 过滤器
        transports = {}, // 分发器
        然后,创建jQuery.ajaxPrefilter和jQuery.ajaxTransport,这两个方法都调用了内部函数addToPrefiltersOrTransports,
        addToPrefiltersOrTransports返回一个匿名闭包函数,这个匿名闭包函数负责将单一前置过滤和单一请求分发器分别放入prefilters和transports。
        我们知道闭包会保持对它所在环境变量的引用,而jQuery.ajaxPrefilter和jQuery.ajaxTransport的实现又完全一样,都是对Map结构的对象进行赋值操作,
        因此这里利用闭包的特性巧妙的将两个方法的实现合二为一。函数addToPrefiltersOrTransports可视为模板模式的一种实现。

    源码分析:

    addToPrefiltersOrTransports
    // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
    //1>>过滤掉 + 开头的     2>>分发器用于区分ajax请求和script标签请求
    function addToPrefiltersOrTransports( structure ) {
    
        //返回一个闭包,通过闭包访问structure,关键在于structure引用哪个对象:能同时引用prefilters和transports
        //dataTypeExpression是可选的 默认为 * 数据类型表达式
        return function(dataTypeExpression,func){
            //修正参数  dataTypeExpression部位字符串默认为 *
            if (typeof dataTypeExpression !== "string"){
                func = dataTypeExpression;
                dataTypeExpression = "*";
            }
            if(jQuery.isFunction(func){
                // 用空格分割数据类型表达式dataTypeExpression
                var dataTypes = dataTypeExpression.toLowerCase().split(rspacesAjax),
                i = 0,
                length = dataTypes.length,//数据类型表达式长度
                dataType,//单个字段 数据类型表达式
                list,
                placeBefore;
                for (;i<length;i++){
                    dataType = dataTypes[i];
                    //如果以+开头 过滤掉 +
                    palceBefore = /^\+/.test(dataType);//选出加号开头的dataType
                    if(palceBefore){
                        dataType = dataType.substr(1) || "*";//从第二个开始或者 *
                    }
                    list = structure[dataType] = structure[dataType] || [];
                    //如果以+ 开头,则插入开始位置  否则添加到末尾
                    //实际操作的是structure
                    list[placeBefore ? "unshift" : "push" ](func);//将函数push进structure
                }
            }
        };
    }

    //prefilters中的前置过滤器在请求发送前,设置请求参数的过程中被调用 调用prefilters的是inspectPrefiltersOrTransports
    //transports也通过这个函数取到与请求类型匹配的请求分发器    inspectPrefiltersOrTransports从prefilters或transports中取到与数据类型匹配的函数数组 然后遍历执行

    源码分析:

    inspectPrefiltersOrTransports
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
            dataType /* internal */, inspected /* internal */ ) {
    
        dataType = dataType || options.dataTypes[ 0 ];
        inspected = inspected || {};
    
        inspected[ dataType ] = true;
    
        var list = structure[ dataType ],
            i = 0,
            length = list ? list.length : 0,
            executeOnly = ( structure === prefilters ),
            selection;
    
        for(; i < length && ( executeOnly || !selection ); i++ ) {
            selection = list[ i ]( options, originalOptions, jqXHR );//遍历数组
            // If we got redirected to another dataType
            // we try there if executing only and not done already
            if ( typeof selection === "string" ) {
                if ( !executeOnly || inspected[ selection ] ) {
                    selection = undefined;
                } else {
                    options.dataTypes.unshift( selection );
                    selection = inspectPrefiltersOrTransports(
                            structure, options, originalOptions, jqXHR, selection, inspected );
                }
            }
        }
        // If we're only executing or nothing was selected
        // we try the catchall dataType if not done already
        if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
            selection = inspectPrefiltersOrTransports(
                    structure, options, originalOptions, jqXHR, "*", inspected );
        }
        // unnecessary when only executing (prefilters)
        // but it'll be ignored by the caller in that case
        return selection;
    }

    类型转换器:


        *         text          window.String         任意内容转换成字符串
        text     html           true                文本转换成THML
        text    json        jQuery.parseJSON    文本转换成JSON
        text    script        globalEval(text);    用eval执行text
        text    xml            jQuery.parseXML        文本转换为XML

        类型转换器   将responseText或responseXML转换成请求时指定的数据类型dataType
        如果没有指定就一句响应头Content-Type自动猜测一个
        转换过程是:如果A>B  首先查找 A>B  ,如果没有找到  查找C  将A转换成C  然后将C再转换成B

    源码分析:

    ajaxConvert
    function ajaxConvert( s, response ) {
    
        // Apply the dataFilter if provided
        if ( s.dataFilter ) {
            response = s.dataFilter( response, s.dataType );
        }
    
        var dataTypes = s.dataTypes,
            converters = {},
            i,
            key,
            length = dataTypes.length,
            tmp,
            // Current and previous dataTypes
            current = dataTypes[ 0 ],
            prev,
            // Conversion expression
            conversion,
            // Conversion function
            conv,
            // Conversion functions (transitive conversion)
            conv1,
            conv2;
    
        // For each dataType in the chain
        for( i = 1; i < length; i++ ) {
    
            // Create converters map
            // with lowercased keys
            if ( i === 1 ) {
                for( key in s.converters ) {
                    if( typeof key === "string" ) {
                        converters[ key.toLowerCase() ] = s.converters[ key ];
                    }
                }
            }
    
            // Get the dataTypes
            prev = current;
            current = dataTypes[ i ];
    
            // If current is auto dataType, update it to prev
            if( current === "*" ) {
                current = prev;
            // If no auto and dataTypes are actually different
            } else if ( prev !== "*" && prev !== current ) {
    
                // Get the converter
                conversion = prev + " " + current;
                conv = converters[ conversion ] || converters[ "* " + current ];
    
                // If there is no direct converter, search transitively
                if ( !conv ) {
                    conv2 = undefined;
                    for( conv1 in converters ) {
                        tmp = conv1.split( " " );
                        if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
                            conv2 = converters[ tmp[1] + " " + current ];
                            if ( conv2 ) {
                                conv1 = converters[ conv1 ];
                                if ( conv1 === true ) {
                                    conv = conv2;
                                } else if ( conv2 === true ) {
                                    conv = conv1;
                                }
                                break;
                            }
                        }
                    }
                }
                // If we found no converter, dispatch an error
                if ( !( conv || conv2 ) ) {
                    jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
                }
                // If found converter is not an equivalence
                if ( conv !== true ) {
                    // Convert with 1 or 2 converters accordingly
                    response = conv ? conv( response ) : conv2( conv1(response) );
                }
            }
        }
        return response;
    }

     扩展生成的ajax方法:

    jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
        jQuery.fn[ o ] = function( f ){
            return this.bind( o, f );//生成的函数 AJAX 事件
        };
    });
    //each 调用ajax实现get和post方法
    jQuery.each( [ "get", "post" ], function( i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {
            //没有传入参数
            if ( jQuery.isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = undefined;
            }
    
            return jQuery.ajax({
                type: method,
                url: url,
                data: data,
                success: callback,
                dataType: type
            });
        };
    });

    调用get方法获取js或json的方法:

        //jQuery自带的加载js方法,只存在一次请求中,$.getScript("free.js",callback);只能在callback中使用js 且被压缩了
        getScript: function( url, callback ) {
            return jQuery.get(url,undefined,callback,"script");
        },
        //引用JSON文件
        getJSON: function( url, data, callback ) {
            return jQuery.get(url,undefined,callback,"script");
        },

     

     

  • 相关阅读:
    用移动硬盘代替DVD安装单系统Vista方法
    背完这444句,你的口语绝对不成问题了
    DataGridView 只能输入整数解决方案
    转载:Firefox的失败在中国几乎就是命中注定
    ZBlog 添加运行天数
    并行和串行通信
    ZBlog 添加收藏本站
    ITPUB调查高达42%的DBA由开发人员转变而成
    DataGridView 只能输入整数解决方案
    用移动硬盘代替DVD安装单系统Vista方法
  • 原文地址:https://www.cnblogs.com/colorstory/p/2631332.html
Copyright © 2020-2023  润新知