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方法,添加預過濾器和分發器來完成包裝。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM