原文: http://www.ajaxbbs.net/post/webFront/JS-Array-type-check.html

有关对数组类型对象的检测,一直以来基本都是依靠instanceof来检测的,用instanceof可以满足几乎大多数的需要,但是对于跨页面的检测就有问题了,这个问题以前一直没有注意过也没有遇到过,但今天在ajaxian又看到了对这个问题的讨论,觉得自己有必要在这里总结一下。

对于原作者的博客文章可以看这里:http://thinkweb2.com/projects/prototype/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

  1. typeof是检测能力最弱的一种检测方式。

    typeof对类型的检测只返回这几种结果:number、string、boolean、object、function及undefined。对于基本数据类型的检测还是可以满足的,但对于对象类型检测就有问题了,如果你测试typeof null、typeof {}、typeof window、typeof []、typeof new Date()发现他们都返回object。对于typeof的具体介绍可以参看这篇文章:http://www.freeage.cn/article.asp?id=178

    对于null类型我们可以通过严格相等比较即可:value===null,但对于数组就没有这么简单了。

  2. 使用instanceof运算符或者constructor来检测

    instanceof的检测原理为:运算符左侧对象的原型链是否和右侧对象的prototype属性是同一个对象。因此这就存在问题了,假如左右侧的对象不在一个页面中,这样他们的引用就不太可能是一个对象了。典型的示例就是在存在frame的页面中,如a.html通过iframe包含b.html,如果在a.html中定义var arr=[];然后再b.html中通过parent.arr instanceof Array就会返回false,因为parent.arr的原型链是和parent.Array对象的prototype属性引用的同一个对象,而和b.html中的Array的prototype属性完全没有关系。

    我们看到了instanceof对于数组类型检测存在的局限性:必须在同一个页面中的对象才能正确检测。其实采用constructor属性来判断和instanceof的局限性是一样的,因为他们都是对比的对象引用是否一样。

  3. 特性检测(Duck-typing)

    采用简单的假设方式来做检测,如数组对象存在concat、splice、sort等相比其它对象特有的一些特性,因此可以通过简单判断对象是否有这些特性来进行检测。

    如下面的一些实现例子:
    //prototype1.5的实现 
    function isArray(object) { 
      return object != null && typeof object === "object" && 
        'splice' in object && 'join' in object;  

    //Douglas Crockford在2003年时提供的一种方式 
    typeof myArray.sort == 'function' 

    这种实现方式虽然避免了第二种方式的问题,但它所存在的问题也显而易见,在JS中,对于object我们很容易为其增加任何我们需要的属性(方法),因此假如我们为一个object对象增加了一些数组所特有的属性,检测就不准确了。

    看下面的代码:
    var myArray = { splice: 1, join: 2 ,sort:function(){}};  
    Object.isArray(myArray); // true 
    typeof myArray.sort == 'function' //true 

    两种检测方式都返回了错误的结果:true。因此这种方式的检测仍然不是一种完美的解决办法。下面就是我们要说的一种由kangax提出的一个比较完美的解决办法。

  4. 采用Object.prototype.toString.call(o)来检测

    为什么要用Object.prototype.toString而不是Function.prototype.toString或者其它?这是和他们的toString解释方式有关系的。下面是ECMA中对Object.prototype.toString的解释:

    Object.prototype.toString( )
    When the toString method is called, the following steps are taken:
    1. Get the [[Class]] property of this object.
    2. Compute a string value by concatenating the three strings “[object “, Result (1), and “]”.
    3. Return Result (2)

    其过程简单说来就是:1、获取对象的类名(对象类型)。2、然后将[object、获取的类名、]组合并返回。

    ECMA中对Array有如下说明:

    The [[Class]] property of the newly constructed object is set to “Array”.

    因此我们用如下代码来检测数组:

    function isArray(o) { 
      return Object.prototype.toString.call(o) === '[object Array]';  
    }

    这种方式既解决了instanceof存在的跨页面问题,也解决了属性检测方式所存在的问题,实在是一种妙招,一个很好的解决方案。

    除此之外,这种解决办法也可以应用于判断Date,Function等类型的对象。

参考 : https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray

posted on 2011-07-20 01:56  星光~  阅读(398)  评论(0编辑  收藏  举报