zoukankan      html  css  js  c++  java
  • jQuery ajax —— 将类AJAX方法包装起来

    上一篇文章,将jQuery.ajax中的一些细节补充完。这篇文章讲解如果将类AJAX方法都包装进jQuery.ajax中。下篇文章则讲解各预过滤器和分发器的细节。

    为什么要包装起来? 

    我们知道,古老的XMLHttpRequest出于同源策略考虑,是不支持跨域的。所以,在前端想动态加载跨域Javascript脚本,通常是使用被称为Script DOM Element的方案,如:

    var scriptElem = document.createElement("script");
    scriptElem.src = "http://anydomain.com/A.js";
    document.getElementsByTagName("head")[0].appendChild(scriptElem);

    同理,JSON也无法通过XMLHttpRequest进行跨域,所以利用Script DOM Element,将JSON填入一个回调函数中来实现其跨域,也就是JSONP(JSON with padding, 填充式JSON或参数式JSON)。

    实际上JSONP就是将,得到JSON后回调的函数通过GET传参告诉服务器,然后服务器拼接一段脚本,用该回调函数并参数为需要的JSON数据,如:

    callback({ "name": "Justany_WhiteSnow" });

    jQuery团队当然希望开发者开发的时候,不需要想需不需要跨域,只要直接使用就行了。

    所以他们把XMLHttpRequest、Script DOM Element、JSONP包装起来,都当成AJAX来使用。

    这里顺便提一下,其实现代浏览器(Firefox 3.5+、Safari 4+、Chrome等)中,通过XMLHttpRequeest实现了CORS(Cross-Origin Resource Sharing, 跨源资源共享)原生支持。也就是XMLHttpRequest在某些浏览器中,实际上是可以跨域的,只需要设置一下HTTP Response Header中的Access-Control-Allow-Origin。比如设置成通配符*。

    而IE8也引入XDomainRequest也实现了CORS。

    但毕竟某些浏览器不行,所以,咳咳……这不能成为一种通用方案。

    怎么包装起来? 

    首先我们有一个山寨XHR对象,也就是jqXHR对象。通过对其添加send、abort来模拟XHR对象。

    可是我们需要在不同方案执行前先处理一下特异性的东东,所以我们需要一个预过滤机制(Prefilter)来预先处理一下。

    然后我们需要知道到底应当用那一套方案来执行整个过程,所以我们需要一个分发机制(Transport)来得到最后的jqXHR对象。

    inspectPrefiltersOrTransports

    我们在jQuery.ajax找到了预过滤和分发机制的函数,inspectPrefiltersOrTransports。

    // 预过滤
    inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
    
    //……
    
    // 得到transport
    transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );

    然后我们看看这个函数在干些什么。

    // 检测函数,预过滤或者分发器
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
    
        // 定义个参数对象
        var inspected = {},
            // 看看传进来的结构体是prefilters, 还是transports
            // 如果是prefilters,证明这是预过滤过程,如果是transports分发过程
            // 所以这个是,是不是在分发过程
            seekingTransport = ( structure === transports );
    
        function inspect( dataType ) {
            var selected;
            // 将inspected的dataType对应属性设置成true
            inspected[ dataType ] = true;
            // 遍历prefilters对应dataType对象中的所有过滤器或者转换器工厂函数
            jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
                // 得到dataType或者转换器
                var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
                // 如果dataType为字符串,即上面过程是过滤器
                // 如果在预过滤过程
                // 并且这个过滤出来的dataType不等于刚开始传进来的dataType
                if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
                    // 将现在这个新的dataType插入到options中
                    options.dataTypes.unshift( dataTypeOrTransport );
                    // 检测新的dataType
                    inspect( dataTypeOrTransport );
                    return false;
                // 否则如果在分发过程
                } else if ( seekingTransport ) {
                    // 定义selected为dataTypeOrTransport
                    return !( selected = dataTypeOrTransport );
                }
            });
            
            return selected;
        }
    
        // 检查dataTypes数组的第一个,如果结果是undefined,
        // 则看看上面检查的是不是通配符*,如果不是则检查通配符
        return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
    }

    我们可以看到,这个函数实际上就是从prefilters和transports中取出对应dataType的东东,然后过滤或者分发。

    那么怎么定义prefilters和transports这两个对象的呢?

    jQuery.ajaxPrefilter & jQuery.ajaxTransport

    实际上,jQuery是通过这两个接口来定义上面两个对象的内容的,一个是定义预过滤器,另一个则定义分发器。

    jQuery.ajaxPrefilter = addToPrefiltersOrTransports( prefilters );
    jQuery.ajaxTransport = addToPrefiltersOrTransports( transports );

    而这两个方法都是由addToPrefiltersOrTransports生成的。

    addToPrefiltersOrTransports

    // jQuery.ajaxPrefilter和jQuery.ajaxTransport的构造函数
    function addToPrefiltersOrTransports( structure ) {
    
        // dataTypeExpression是可选参数,缺省值为*
        return function( dataTypeExpression, func ) {
            
            // 如果dataTypeExpression不是字符串
            // 模拟重载
            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                // 缺省为*
                dataTypeExpression = "*";
            }
    
            var dataType,
                i = 0,
                // 将dataTypeExpression转为小写,并用空白拆成数组
                dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
    
            // 如果func是一个函数
            if ( jQuery.isFunction( func ) ) {
                // 遍历dataTypeExpression中的所有dataType
                while ( (dataType = dataTypes[i++]) ) {
                    // 如果有必要则删除头部的+号
                    if ( dataType[0] === "+" ) {
                        // 删除+号
                        dataType = dataType.slice( 1 ) || "*";
                        // 往structure对应的dataType中推入func函数
                        (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
    
                    // 否则不需要处理
                    } else {
                        // 往structure对应的dataType中推入func函数
                        (structure[ dataType ] = structure[ dataType ] || []).push( func );
                    }
                }
            }
        };
    }

    接下来就是通过调用jQuery.ajaxPrefilter和jQuery.ajaxTransport方法,添加预过滤器和分发器来完成包装。

  • 相关阅读:
    LeetCode 32. 最长有效括号(Longest Valid Parentheses)
    LeetCode 141. 环形链表(Linked List Cycle)
    LeetCode 160. 相交链表(Intersection of Two Linked Lists)
    LeetCode 112. 路径总和(Path Sum)
    LeetCode 124. 二叉树中的最大路径和(Binary Tree Maximum Path Sum)
    LightGBM新特性总结
    sql service 事务与锁
    C#泛型实例详解
    C# 中的委托和事件(详解)
    C# DateTime日期格式化
  • 原文地址:https://www.cnblogs.com/justany/p/2981523.html
Copyright © 2011-2022 走看看