web2.0的標誌是Ajax的非同步通訊的發掘,給我們帶來像google map,google suggest
這樣令人驚歎的東西。而Ajax的核心就是那個XMLhttp對象(當然,如果用iframe也可以類比出來,但不在本文議題之內)。像往常一樣,IE發明了東西,然後被標準瀏覽器抄去,發揚光大了,IE的卻不如那麼冒牌貨,如IE的innerHTML,私自去掉空白,IE的TextRange,只能操作文本,W3C卻更為強悍,雖然沒什麼人用,IE的eot字型技術,W3C則有@font-face……IE的XMLhttp對象也是這樣,它是一個為人詬病的ActiveXObject,更要病的是它版本太多了,Msxml2.XMLHTTP.6.0,Msxml2.XMLHTTP.5.0,Msxml2.XMLHTTP.4.0,Msxml2.XMLHTTP.3.0,Msxml2.XMLHTTP,Microsoft.XMLHTTP,越新的版本功能越多,這還不算上IE7的原生XMLHttpRequest對象,IE8的XDomainRequest。W3C那邊不管這麼多,名字都是固定的,只要升級瀏覽器就好了。這樣就搞得IE那邊分支龐大,而且IE一方都是老不死,國內學校裡面還有大批window 2000在服役,換言之,IE5.5也不能無視。如何獲得建立ActiveXObject的ProgID是重中之重中。問題是,裡面也有陷阱,下面是正確的建立XMLHttp對象的ProgID數組:
var xmlhttp_progids = ["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP", "Microsoft.XMLHTTP" ]; //如果用我的超級數組對象,就能很簡單地找出正確的xmlhttp_progid var xmlhttp_progid = dom.array( xmlhttp_progids).one(function(v){ return new ActiveXObject(v) }); alert(xmlhttp_progid)
撇開我的超級數組不談,明顯當中可供選擇的ProgID少許多,像Msxml2.XMLHTTP.5.0,Msxml2.XMLHTTP.4.0不見了。為什麼呢?且聽我一一道來。可能大家在網上還看到Msxml2.XMLHTTP.7.0,很遺念,那隻是以訛傳訛,我用IE8測試並不存在這個版本。Msxml2.XMLHTTP.5.0,Msxml2.XMLHTTP.4.0根據我手頭上掌握的資料,它們並不是瀏覽器用的,屬於旁支,存在一定的相容問題。其中5.0是為office所開發的,甚至帶有一些特性是後來的6.0所沒有的(如xml數字加密)。
MSXML 4.0 is a separate download that was released by Microsoft in October 2001. The latest or current service pack release of MSXML 4.0 is available through the Microsoft Web site. MSXML 4.0 must be installed separately and is not currently included with other Microsoft products.
MSXML 4.0 installs side-by-side with earlier versions of MSXML without affecting any existing functionality.
MSXML 5.0 for Microsoft Office Applications is only available with current versions of Microsoft Office.
MSXML 5.0 for Microsoft Office Applications installs side-by-side with earlier versions of MSXML without affecting any existing functionality.
那Msxml2.XMLHTTP.3.0是怎麼回事呢?根據IE blog的建議,應該僅使用6.0和3.0,不要使用老的microsoft.xmlhttp,那它也應該出現上面的數組中。此外還有一個MSXML2.XMLHTTP.2.6,但不知咋搞的,總之,如果你的遊覽器打了某些升級補丁,new ActiveXObject("Msxml2.XMLHTTP") 調用的是2.6或3.0版本,非常混亂。此後的版本,才正確對應它的版本號碼。因此這裡沒有列舉MSXML2.XMLHTTP.2.6與MSXML2.XMLHTTP.3.0的必要,都給Msxml2.XMLHTTP代表了。
在IE7中,微軟決定支援W3C的XMLHttp對象,其實是換了外套而已,它是基於3.0版。不過它出於安全的考量,它不允許訪問相對路徑下的本地檔案系統。
從各大類庫的情形看來,Msxml2.XMLHTTP.6.0沒有什麼用到,因為如果微軟真的沿著沿著W3C的路線的話,那麼IE8中的原生XMLHttp對象應該自動調用Msxml2.XMLHTTP.6.0(它給XMLHttpRequest代表了,三個代表的思想影響深遠啊)。下面是幾大類庫的源碼:
//*********************jQuery1.4a2****************** xhr: function(){ return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); }, //*********************mootools1.2.4****************** Browser.Request = function(){ return $try(function(){ return new XMLHttpRequest(); }, function(){ return new ActiveXObject('MSXML2.XMLHTTP'); }, function(){ return new ActiveXObject('Microsoft.XMLHTTP'); }); }; //***********************prototype1.61rc2******************* getTransport: function() { return Try.these( function() {return new XMLHttpRequest()}, function() {return new ActiveXObject('Msxml2.XMLHTTP')}, function() {return new ActiveXObject('Microsoft.XMLHTTP')} ) || false; },
不過我的也不賴,結合我的緩衝系統更為強悍,這樣以下產生XMLhttp對象就不用檢測該用何種產生方式,直接從緩衝裡找就是:
var xhr = function(){ if(!arguments.callee.single){ arguments.callee.single = true; var fn =dom.array(function(){return new XMLHttpRequest()},//IE7,IE8,w3c function() {return new ActiveXObject('Msxml2.XMLHTTP')},//IE6 function() {return new ActiveXObject('Microsoft.XMLHTTP')}).one(function(fn){//IE5 return fn(); }); dom.cache("dom-xhr","dom-xhr",fn);//緩衝產生XMLHttp對象的函數 return fn(); }else{ return dom.cache("dom-xhr","dom-xhr")();//從緩衝擷取產生XMLHttp對象 } } var xhrObject = xhr();//調用 alert(xhrObject)//[object XMLHttpRequest]
如果不用我的超級數組對象,這裡也提供一種原始的方式:
var xhr = function() { if(!arguments.callee.single){ var fns = [ function () { return new XMLHttpRequest(); }, function () { return new ActiveXObject('Msxml2.XMLHTTP'); }, function () { return new ActiveXObject('Microsoft.XMLHTTP'); }, ]; for (var i = 0,n=fns.length; i < n; i++) { try { fns[i](); arguments.callee.single = fns[i]; break; }catch(e){} } return arguments.callee.single(); }else{ return arguments.callee.single(); } } var xhrObject = xhr();//調用 alert(xhrObject) //[object XMLHttpRequest]
但即使我們緩衝了產生方式,每次還要判斷一下arguments.callee.single的值,能否寫得更優雅更有效率呢?!能,見我《隨性函數》一文。
var xhr = function(){ xhr = dom.array(function(){return new XMLHttpRequest()},//IE7,IE8,w3c function() {return new ActiveXObject('Msxml2.XMLHTTP')},//IE6 function() {return new ActiveXObject('Microsoft.XMLHTTP')}).one(function(fn){//IE5 return fn(); }); return xhr(); }//****************或者**************** var xhr = function() { var fns = [ function () { return new XMLHttpRequest(); }, function () { return new ActiveXObject('Msxml2.XMLHTTP'); }, function () { return new ActiveXObject('Microsoft.XMLHTTP'); }, ]; for (var i = 0,n=fns.length; i < n; i++) { try { fns[i](); xhr = fns[i];//注意這裡,用於重設函數 break; }catch(e){} } return xhr() }
我們可以測試一下,在中間加一個alert,看它有沒有重複資料偵測產生函數。
<br /> var xhr = function() {<br /> var fns = [<br /> function () { return new XMLHttpRequest(); },<br /> function () { return new ActiveXObject('Msxml2.XMLHTTP'); },<br /> function () { return new ActiveXObject('Microsoft.XMLHTTP'); },<br /> ];<br /> alert("dddddd")<br /> for (var i = 0,n=fns.length; i < n; i++) {<br /> try {<br /> fns[i]();<br /> xhr = fns[i];//注意這裡,用於重設函數<br /> break;<br /> }catch(e){}<br /> }<br /> return xhr()<br /> };<br /> var xhrObject = xhr();//調用<br /> alert(xhrObject)<br /> var xhrObject2 = xhr();//調用<br /> alert(xhrObject2)<br /> var xhrObject3 = xhr();//調用<br /> alert(xhrObject3)<br />
運行代碼
XDomainRequest不討論,資料太少,跨域現在方法很多,CSS跨域,圖片跨域,動態script標籤,window.name……本文已完美地解決了原議題,利用隋性函數設計了一個高效擷取XMLhttp對象的方法,本文就到此為止了。