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) {
这种方式既解决了instanceof存在的跨页面问题,也解决了属性检测方式所存在的问题,实在是一种妙招,一个很好的解决方案。除此之外,这种解决办法也可以应用于判断Date,Function等类型的对象。所以可以扩展判断很多类型的函数。
return Object.prototype.toString.call(o) === '[object Array]';
}
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://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/