如何检测变量是不是为数组
Posted
技术标签:
【中文标题】如何检测变量是不是为数组【英文标题】:How to detect if a variable is an array如何检测变量是否为数组 【发布时间】:2010-11-06 16:53:06 【问题描述】:确定 javascript 中的变量是否为数组的最佳事实上的标准跨浏览器方法是什么?
在网上搜索有许多不同的建议,有的不错,有的无效。
例如,下面是一个基本的方法:
function isArray(obj)
return (obj && obj.length);
但是,请注意如果数组为空,或者 obj 实际上不是数组但实现了长度属性等会发生什么。
那么就实际工作、跨浏览器和仍然高效执行而言,哪种实现是最好的?
【问题讨论】:
这不会在字符串上返回 true 吗? 给出的例子并不是为了回答问题本身,只是一个如何解决解决方案的例子——在特殊情况下通常会失败(比如这个,因此“但是,请注意...”)。 @James:在大多数浏览器(IE 除外)中,字符串是类似数组的(即可以通过数字索引访问) 不能'相信这很难做到...... How do you check if a variable is an array in JavaScript? 的可能重复项 【参考方案1】:JS中对象的类型检查是通过instanceof
完成的,即
obj instanceof Array
如果对象跨越帧边界,这将不起作用,因为每个帧都有自己的Array
对象。您可以通过检查对象的内部 [[Class]] 属性来解决此问题。要获取它,请使用Object.prototype.toString()
(ECMA-262 保证可以使用):
Object.prototype.toString.call(obj) === '[object Array]'
这两种方法都只适用于实际数组,而不适用于类似数组的对象,如 arguments
对象或节点列表。由于所有类似数组的对象都必须有一个数字 length
属性,我会像这样检查这些:
typeof obj !== 'undefined' && obj !== null && typeof obj.length === 'number'
请注意,字符串将通过此检查,这可能会导致问题,因为 IE 不允许通过索引访问字符串的字符。因此,您可能希望将 typeof obj !== 'undefined'
更改为 typeof obj === 'object'
以排除类型与 'object'
不同的原语和宿主对象。这仍然会让字符串对象通过,必须手动排除。
在大多数情况下,您真正想知道的是您是否可以通过数字索引迭代对象。因此,检查对象是否具有名为 0
的属性可能是个好主意,这可以通过以下检查之一来完成:
typeof obj[0] !== 'undefined' // false negative for `obj[0] = undefined`
obj.hasOwnProperty('0') // exclude array-likes with inherited entries
'0' in Object(obj) // include array-likes with inherited entries
转换为对象对于类似数组的原语(即字符串)正常工作是必要的。
这是对 JS 数组进行稳健检查的代码:
function isArray(obj)
return Object.prototype.toString.call(obj) === '[object Array]';
和可迭代(即非空)类数组对象:
function isNonEmptyArrayLike(obj)
try // don't bother with `typeof` - just access `length` and `catch`
return obj.length > 0 && '0' in Object(obj);
catch(e)
return false;
【讨论】:
对 js 数组检查的最新技术的伟大总结。 从 MS JS 5.6 (IE6?) 开始,“instanceof”运算符在针对 COM 对象 (ActiveXObject) 运行时会泄漏大量内存。尚未检查 JS 5.7 或 JS 5.8,但这可能仍然适用。 @James:有趣——我不知道这个泄漏;无论如何,有一个简单的解决方法:在 IE 中,只有原生 JS 对象有hasOwnProperty
方法,所以只需在 instanceof
前面加上 obj.hasOwnProperty &&
;另外,这仍然是IE7的问题吗?我通过任务管理器进行的简单测试表明,在最小化浏览器后内存被回收......
@Christoph - 不确定 IE7,但 IIRC 这不在 JS 5.7 或 5.8 的错误修复列表中。我们将底层 JS 引擎托管在服务端的服务中,因此最小化 UI 是不适用的。
@TravisJ:见ECMA-262 5.1, section 15.2.4.2;内部类名按照惯例是大写的 - 请参阅section 8.6.2【参考方案2】:
ECMAScript 第 5 版的到来为我们提供了测试变量是否为数组的最可靠的方法,Array.isArray():
Array.isArray([]); // true
虽然此处接受的答案适用于大多数浏览器的框架和窗口,但不适用于 Internet Explorer 7 及更低版本,因为从不同窗口调用数组的 Object.prototype.toString
将返回[object Object]
,不是[object Array]
。 IE 9 似乎也回归到了这种行为(请参阅下面的更新修复)。
如果您想要一个适用于所有浏览器的解决方案,您可以使用:
(function ()
var toString = Object.prototype.toString,
strArray = Array.toString(),
jscript = /*@cc_on @_jscript_version @*/ +0;
// jscript will be 0 for browsers other than IE
if (!jscript)
Array.isArray = Array.isArray || function (obj)
return toString.call(obj) == "[object Array]";
else
Array.isArray = function (obj)
return "constructor" in obj && String(obj.constructor) == strArray;
)();
它并非完全牢不可破,但只有努力破坏它的人才能将其破坏。它可以解决 IE7 及更低版本和 IE9 中的问题。 The bug still exists in IE 10 PP2,但它可能会在发布前修复。
PS,如果您不确定解决方案,那么我建议您根据自己的内心内容对其进行测试和/或阅读博客文章。如果您不习惯使用条件编译,还有其他潜在的解决方案。
【讨论】:
The accepted answer 在 IE8+ 中可以正常工作,但在 IE6,7 中不行 @Pumbaa80:你是对的 :-) IE8 解决了它自己的 Object.prototype.toString 的问题,但是测试了一个在 IE8 文档模式下创建的数组,该数组被传递给 IE7 或更低版本的文档模式,问题依旧。我只测试了这种情况,而不是相反。我已编辑将此修复程序仅应用于 IE7 及更低版本。 WAAAAAAA,我讨厌 IE。我完全忘记了不同的“文档模式”或“分裂模式”,就像我要称呼它们的那样。 虽然想法很棒而且看起来不错,但在带有弹出窗口的 IE9 中这对我不起作用。它为开瓶器创建的数组返回 false... 有 IE9 兼容的解决方案吗? (问题似乎是 IE9 实现了 Array.isArray 本身,它在给定的情况下返回 false,而它不应该返回。 @Steffen:很有趣,所以isArray
的本机实现不会从其他文档模式中创建的数组返回true?当我有时间时,我将不得不对此进行研究,但我认为最好的办法是在 Connect 上提交一个错误,以便可以在 IE 10 中修复它。【参考方案3】:
Crockford 在“The Good Parts”的第 106 页有两个答案。第一个检查构造函数,但会在不同的框架或窗口中给出假阴性。这是第二个:
if (my_value && typeof my_value === 'object' &&
typeof my_value.length === 'number' &&
!(my_value.propertyIsEnumerable('length'))
// my_value is truly an array!
Crockford 指出此版本会将arguments
数组识别为数组,即使它没有任何数组方法。
他对这个问题的有趣讨论从第 105 页开始。
还有更多有趣的讨论(发布好的部分)here,其中包括此提案:
var isArray = function (o)
return (o instanceof Array) ||
(Object.prototype.toString.apply(o) === '[object Array]');
;
所有的讨论让我永远不想知道某个东西是否是一个数组。
【讨论】:
这将在 IE 中中断字符串对象并排除字符串原语,这些原语在 IE 中是类似数组的;如果你想要实际的数组,检查 [[Class]] 会更好;如果你想要类似数组的对象,检查太严格了 @Christoph——我通过编辑添加了更多内容。有趣的话题。【参考方案4】:jQuery 实现了一个 isArray 函数,这表明最好的方法是
function isArray( obj )
return toString.call(obj) === "[object Array]";
(sn-p 取自 jQuery v1.3.2 - 稍作调整以脱离上下文)
【讨论】:
他们在 IE 上返回 false (#2968)。 (来自jquery源) jQuery源码中的那条注释似乎是指isFunction函数,而不是isArray 你应该使用Object.prototype.toString()
- 这不太可能打破【参考方案5】:
从大师 John Resig 和 jquery 那里窃取数据:
function isArray(array)
if ( toString.call(array) === "[object Array]")
return true;
else if ( typeof array.length === "number" )
return true;
return false;
【讨论】:
第二个测试也会为字符串返回 true:typeof "abc".length === "number" // true 另外,我猜你永远不应该硬编码类型名称,比如“数字”。尝试将其与实际数字进行比较,例如 typeof(obj) == typeof(42) @mtod:为什么不应该对类型名称进行硬编码?毕竟typeof
的返回值是标准化的?【参考方案6】:
一旦你确定它是一个数组,你打算如何处理它?
例如,如果您打算枚举包含的值,如果它看起来像一个数组,或者如果它是一个用作哈希表的对象,那么下面的代码会得到你想要的(当闭包函数返回“未定义”以外的任何内容时,此代码将停止。请注意,它不会迭代 COM 容器或枚举;这留给读者作为练习):
function iteratei( o, closure )
if( o != null && o.hasOwnProperty )
for( var ix in seq )
var ret = closure.call( this, ix, o[ix] );
if( undefined !== ret )
return ret;
return undefined;
(注意:“o != null”测试 null 和 undefined)
使用示例:
// Find first element who's value equals "what" in an array
var b = iteratei( ["who", "what", "when" "where"],
function( ix, v )
return v == "what" ? true : undefined;
);
// Iterate over only this objects' properties, not the prototypes'
function iterateiOwnProperties( o, closure )
return iteratei( o, function(ix,v)
if( o.hasOwnProperty(ix) )
return closure.call( this, ix, o[ix] );
)
【讨论】:
虽然答案可能很有趣,但它与问题没有任何关系(顺便说一句:通过for..in
遍历数组是不好的[tm])
@Christoph - 当然可以。必须有一些理由来决定某个东西是否是一个数组:因为你想对这些值做一些事情。最典型的事情(至少在我的代码中)是映射、过滤、搜索或以其他方式转换数组中的数据。上面的函数正是这样做的:如果传递的东西有可以迭代的元素,那么它就会迭代它们。如果不是,那么它什么也不做,并且无害地返回 undefined。
@Christoph - 为什么用 for..in bad[tm] 迭代数组?你还会如何迭代数组和/或哈希表(对象)?
@James: for..in
迭代对象的可枚举属性;你不应该将它与数组一起使用,因为:(1)它很慢; (二)不能保证维持秩序; (3) 它将包含在对象或其任何原型中设置的任何用户定义的属性,因为 ES3 不包含任何设置 DontEnum 属性的方法;可能还有其他问题让我忘记了
@Christoph - 另一方面,对于稀疏数组,使用 for(;;) 将无法正常工作,并且它不会迭代对象属性。由于您提到的原因,#3 被认为是 Object 的错误形式。另一方面,您在性能方面是正确的:for..in 在 1M 元素数组上比 for(;;) 慢约 36 倍。哇。不幸的是,不适用于我们的主要用例,即迭代对象属性(哈希表)。【参考方案7】:
为什么不直接使用
Array.isArray(variable)
这是执行此操作的标准方法(感谢 Karl)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
【讨论】:
这适用于 Node.js 和浏览器,不仅仅是 CouchDB:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…【参考方案8】:如果你想要跨浏览器,你想要jQuery.isArray。
【讨论】:
【参考方案9】:在w3school 上有一个例子应该很标准。
为了检查一个变量是否是一个数组,他们使用类似于这个的东西
function arrayCheck(obj)
return obj && (obj.constructor==Array);
在 Chrome、Firefox、Safari、ie7 上测试
【讨论】:
使用constructor
进行类型检查太脆弱了;改用建议的替代方法之一
你为什么这么认为?关于脆?
@Kamarey: constructor
是原型对象的常规 DontEnum 属性;只要没有人做任何愚蠢的事情,对于内置类型来说这可能不是问题,但对于用户定义的类型来说,这很容易;我的建议:总是使用instanceof
,它检查原型链并且不依赖可以任意覆盖的属性
谢谢,在这里找到另一种解释:thinkweb2.com/projects/prototype/…
这不可靠,因为 Array 对象本身可以被自定义对象覆盖。【参考方案10】:
可以在phpJS site 上找到该功能的最佳研究和讨论版本之一。您可以链接到软件包,也可以转到function directly。我强烈推荐该站点以在 JavaScript 中构建良好的 PHP 函数等效项。
【讨论】:
【参考方案11】:没有足够的引用相等的构造函数。有时他们对构造函数有不同的引用。所以我使用它们的字符串表示。
function isArray(o)
return o.constructor.toString() === [].constructor.toString();
【讨论】:
被constructor:toString:function() return "function Array() [native code] ";
愚弄【参考方案12】:
将Array.isArray(obj)
替换为obj.constructor==Array
样本:
Array('44','55').constructor==Array
return true (IE8 / Chrome)
'55'.constructor==Array
return false (IE8 / Chrome)
【讨论】:
你为什么要用一个可怕的函数替换正确的函数?以上是关于如何检测变量是不是为数组的主要内容,如果未能解决你的问题,请参考以下文章