Ruby's Louvre

每天学习一点点算法

导航

高效地获取XMLhttp对象

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,看它有没有重复检测生成函数。

XDomainRequest不讨论,资料太少,跨域现在方法很多,CSS跨域,图片跨域,动态script标签,window.name……本文已完美地解决了原议题,利用隋性函数设计了一个高效获取XMLhttp对象的方法,本文就到此为止了。

posted on 2010-01-06 10:47  司徒正美  阅读(7871)  评论(3编辑  收藏  举报