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");
    },

 

 

 

posted on 2013-04-11 17:21  color_story  阅读(734)  评论(0编辑  收藏  举报

导航