确定 JavaScript 中的变量是否为数组的最佳事实上的标准跨浏览器方法是什么?
在网上搜索有许多不同的建议,有些是好的,有些是无效的。
例如,以下是一种基本方法:
function isArray(obj) {
return (obj && obj.length);
}
但是,请注意如果数组为空会发生什么,或者 obj 实际上不是数组但实现了长度属性等。
那么,在实际工作、跨浏览器和仍然有效执行方面,哪种实现是最好的?
确定 JavaScript 中的变量是否为数组的最佳事实上的标准跨浏览器方法是什么?
在网上搜索有许多不同的建议,有些是好的,有些是无效的。
例如,以下是一种基本方法:
function isArray(obj) {
return (obj && obj.length);
}
但是,请注意如果数组为空会发生什么,或者 obj 实际上不是数组但实现了长度属性等。
那么,在实际工作、跨浏览器和仍然有效执行方面,哪种实现是最好的?
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'
alltogether不同的基元和宿主对象。这仍然会让字符串对象通过,这必须手动排除。
在大多数情况下,您真正想知道的是您是否可以通过数字索引迭代对象。因此,检查对象是否具有命名的属性可能是个好主意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;
}
}
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 中的问题。 该错误在 IE 10 PP2 中仍然存在,但可能会在发布前修复。
PS,如果您不确定该解决方案,那么我建议您根据自己的意愿对其进行测试和/或阅读博客文章。如果您对使用条件编译感到不舒服,还有其他潜在的解决方案。
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 页开始。
这里有进一步有趣的讨论(post-Good Parts),其中包括这个提议:
var isArray = function (o) {
return (o instanceof Array) ||
(Object.prototype.toString.apply(o) === '[object Array]');
};
所有的讨论让我永远不想知道某个东西是否是一个数组。
jQuery 实现了一个 isArray 函数,这表明最好的方法是
function isArray( obj ) {
return toString.call(obj) === "[object Array]";
}
(摘自 jQuery v1.3.2 的片段 - 稍微调整以脱离上下文)
从大师 John Resig 和 jquery 那里窃取:
function isArray(array) {
if ( toString.call(array) === "[object Array]") {
return true;
} else if ( typeof array.length === "number" ) {
return true;
}
return false;
}