javascript中对数组的判定浅析

这是司徒正美收集的js判断是否是数组的方法:

http://www.cnblogs.com/rubylouvre/archive/2009/09/15/1567338.html

Douglas Crockford的版本

var isArray = function(a){
  return a &&
    typeof a === 'object' &&
    typeof a.length === 'number' &&
    typeof a.splice === 'function' &&
    !(a.propertyIsEnumerable('length'));
}

Ext与JQuery的版本

var isArray = function(v){
  return Object.prototype.toString.apply(v) === '[object Array]';
}

Prototype的版本

var isArray = function(object) {
  return object != null && typeof object === "object" &&
    'splice' in object && 'join' in object;
}

1、道格拉斯的方法最后一个a.propertyIsEnumerable('length')是一个函数,返回布尔值,该值指示指定属性是否为对象的一部分以及该属性是否是可枚举的。如果 length 存在于 object 中且可以使用一个 For...In 循环枚举出来,则 propertyIsEnumerable 属性将返回 true。如果 object 不具有所指定名称的属性或者所指定的属性不是可枚举的,则propertyIsEnumerable 属性将返回 false。通常,预定义的属性不是可枚举的,而用户定义的属性总是可枚举的。如果a是一个数组,则属性length是不可枚举的,所以返回false,!a.propertyIsEnumerable('length')将返回true,表示是数组。道格拉斯的判断方法即是说如果一个对象有长度属性,有splice方法,并且长度属性是预定义属性而不是用户自定义属性,则a是数组。

2、jquery方法,这个方法很精妙,很多地方都用到了。但是我实在是不太明白为什么这样可以判断是不是一个数组。分析如下:

先来看看toString这个方法吧:JavaScript中toString函数方法是返回对象的字符串表示。使用方法:

objectname. toString([radix])

其中objectname是必选项。要得到字符串表示的对象。radix是可选项。指定将数字值转换为字符串时的进制。JavaScript中toString函数方法是所有内建的 JScript 对象的成员。它的操作依赖于对象的类型:

对象 操作
Array 将 Array 的元素转换为字符串。结果字符串由逗号分隔,且连接起来。
Boolean 如果 Boolean 值是 true,则返回 “true”。否则,返回 “false”。
Date 返回日期的文字表示法。
Error 返回一个包含相关错误消息的字符串。
Function 返回如下格式的字符串,其中 functionname 是被调用 toString 方法函数的名称: function functionname( ) { [native code] }
Number 返回数字的文字表示。
String 返回 String 对象的值。
默认 返回 “[object objectname]”,其中 objectname 是对象类型的名称。

也就是说一个数组执行toString()方法后,返回的是一个有逗号分隔的字符串。var x=[];x.constructor.prototype.toString()等于"";但是Object.prototype.toString()这样用又是什么意思咧?看这篇文章:

http://blog.csdn.net/zhangw428/article/details/4171630

为什么要用Object.prototype.toString而不是Function.prototype.toString或者其它?这是和他们的toString解释方式有关系的。下面是ECMA中对Object.prototype.toString的解释:其过程简单说来就是: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等类型的对象。所以可以扩展判断很多类型的函数。

 

var is = 
{ 
types : ["Array", "Boolean", "Date", "Number", "Object", "RegExp", "String", "Window", "HTMLDocument"] 
} 

for(var i = 0, c; c = is.types[i ++ ]; ) { 
    is[c] = (function(type) { 
          return function(obj) { 
                 return Object.prototype.toString.call(obj) == "[object " + type + "]"; 
          } 
     } )(c); 
} 
alert(is.Array([])); // true 
alert(is.Date(new Date)); // true 
alert(is.RegExp(/reg/ig)); // true 

 

这里说下中间有个is[c]。js中object.name 和object['name']是等价的。但是.存取属性的操作符操作的只能是标识符,而[]操作符操作的可以是变量,字符串或直接量(如1)。

所以这里只能用[]的方法。
3、prototype的方法:

一个非空对象存在splice和join方法就是数组。不过这个方式是有问题的,因为没有判断splice和join方式是不是对象的预定义属性。比如var o = {splice:true,join:true}
isArray(o),这样,判断为true,但实际上他不是数组。修改为:

var isArray = function(object) {
  return object != null && typeof object === "object" &&
    'splice' in object && 'join' in object&&!(object.propertyIsEnumerable('splice'))&&!(object.propertyIsEnumerable('join'));
}

4、再来说说最常用到的方法:

var arr = [];
arr instanceof Array; // true
arr.constructor == Array; //true

typeof arr 方法显然不行,他会返回"object",那instanceof行不行了,正常情况下是行的,如果 object 是 class 或构造函数的实例,则 instanceof 运算符返回 true。如果 object 不是指定类或函数的实例,或者 object 为 null,则返回 false。如: 
[] instanceof Array; // true 
[] instanceof Object; // true 
[] instanceof RegExp; // false 
new Date instanceof Date; // true 
所以,可以用instanceof运算符来判断对象是否为数组类型: 

function isArray(arr) 
{ 
 return arr instanceof Array; 
} 

JavaScript中,每个对象都有一个constructor属性,它引用了初始化该对象的构造函数,常用于判断未知对象的类型。如给定一个求知的值通过typeof运算符来判断它是原始的值还是对象。如果是对象,就可以使用constructor属性来判断其类型。所以判断数组的函数也可以这样写: 

function isArray(arr) 
{ 
return typeof arr == "object" && arr.constructor == Array; 
} 

很多情况下,我们可以使用instanceof运算符或对象的constructor属性来检测对象是否为数组。

刚刚说了,正常情况下可以,说明特殊情况下是不行的,那么什么是特殊情况咧?

<script> 
window.onload=function(){ 
    var iframe_arr=new window.frames[0].Array; 
    alert(iframe_arr instanceof Array); // false 
    alert(iframe_arr.constructor == Array); // false 
} 
</script> 

<body> 
<iframe></iframe> 
</body> 

这个例子是晚上的,结果是对的,说明检查跨框架(cross-frame)页面中的数组时,会失败,原因就是在不同框架(iframe)中创建的数组不会相互共享其prototype属性。

但是我自己写了两个页面测试的时候,又可以,这里希望有高手指点:

<html>
<body>
<script type="text/javascript">

function f(v){
    alert(v instanceof Array)
}
f([]);//true

</script>
<iframe src="2.html"></iframe>
</body>
</html>

iframe页面如下:

<html>
<body>
testshare
<script type="text/javascript">

function xxx(){
    top.parent.f([])
}
</script>
<input type = "button" onclick="xxx()"/>
</body>
</html>

会报错,这里在继续测试。

参考文献:

http://sofish.de/1591

http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

 

 

 

 

 

 

 

 

 

posted on 2012-08-23 01:22  洞庭啸月  阅读(478)  评论(0编辑  收藏  举报

导航