如何检测变量是否为数组

IT技术 javascript arrays
2021-02-08 22:13:07

确定 JavaScript 中的变量是否为数组的最佳事实上的标准跨浏览器方法是什么?

在网上搜索有许多不同的建议,有些是好的,有些是无效的。

例如,以下是一种基本方法:

function isArray(obj) {
    return (obj && obj.length);
}

但是,请注意如果数组为空会发生什么,或者 obj 实际上不是数组但实现了长度属性等。

那么,在实际工作、跨浏览器和仍然有效执行方面,哪种实现是最好的?

6个回答

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;
    }
}
从 MS JS 5.6 (IE6?) 开始,“instanceof”运算符在针对 COM 对象 (ActiveXObject) 运行时会泄漏大量内存。尚未检查 JS 5.7 或 JS 5.8,但这可能仍然适用。
2021-03-16 22:13:07
@James:有趣 - 我不知道这个泄漏;无论如何,有一个简单的解决方法:在 IE 中,只有原生 JS 对象有一个hasOwnProperty方法,所以只需在你的前面instanceof加上obj.hasOwnProperty && ; 另外,这仍然是IE7的问题吗?我通过任务管理器进行的简单测试表明,在最小化浏览器后内存被回收......
2021-03-17 22:13:07
js 数组检查的最新技术的很好的总结。
2021-03-20 22:13:07
@TravisJ:参见ECMA-262 5.1,第 15.2.4.2 节内部类名按照惯例是大写的 - 请参阅第 8.6.2 节
2021-03-27 22:13:07
@Christoph - 不确定 IE7,但 IIRC 这不在 JS 5.7 或 5.8 的错误修正列表中。我们将底层 JS 引擎托管在服务端的服务中,因此最小化 UI 不适用。
2021-04-09 22:13:07

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,如果您不确定该解决方案,那么我建议您根据自己的意愿对其进行测试和/或阅读博客文章。如果您对使用条件编译感到不舒服,还有其他潜在的解决方案。

接受的答案在 IE8+ 中工作正常,但不适用于 IE6,7
2021-03-14 22:13:07
WAAAAAAA,我讨厌IE。我完全忘记了不同的“文档模式”或“分裂模式”,因为我将称它们为。
2021-03-28 22:13:07
@Steffen:有趣,所以 的本机实现isArray不会从以其他文档模式创建的数组返回 true?当我有时间时,我将不得不研究这个问题,但我认为最好的办法是在 Connect 上提交一个错误,以便它可以在 IE 10 中修复。
2021-04-01 22:13:07
虽然这些想法很棒而且看起来不错,但这对我在带有弹出窗口的 IE9 中不起作用。它为打开程序创建的数组返回 false ... 是否有兼容 IE9 的解决方案?(问题似乎是 IE9 实现了 Array.isArray 本身,它在给定的情况下返回 false,而它不应该这样做。
2021-04-04 22:13:07
@Pumbaa80:你是对的:-) IE8 修复了它自己的 Object.prototype.toString 的问题,但测试在 IE8 文档模式下创建的数组传递给 IE7 或更低版本的文档模式,问题仍然存在。我只测试了这种情况,而不是相反。我已编辑将此修复程序仅应用于 IE7 及更低版本。
2021-04-11 22:13:07

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]');
};

所有的讨论让我永远不想知道某个东西是否是一个数组。

@ Christoph--我通过编辑添加了更多内容。引人入胜的话题。
2021-03-26 22:13:07
这将在 IE 中中断字符串对象,并排除字符串基元,除了 IE 之外,它们类似于数组;如果你想要实际的数组,检查 [[Class]] 会更好;如果你想要类似数组的对象,检查太严格了
2021-03-28 22:13:07

jQuery 实现了一个 isArray 函数,这表明最好的方法是

function isArray( obj ) {
    return toString.call(obj) === "[object Array]";
}

(摘自 jQuery v1.3.2 的片段 - 稍微调整以脱离上下文)

他们在 IE 上返回 false (#2968)。(来自jquery源)
2021-03-13 22:13:07
jQuery 源代码中的注释似乎指的是 isFunction 函数,而不是 isArray
2021-03-15 22:13:07
你应该使用Object.prototype.toString()- 这不太可能打破
2021-03-30 22:13:07

从大师 John Resig 和 jquery 那里窃取:

function isArray(array) {
    if ( toString.call(array) === "[object Array]") {
        return true;
    } else if ( typeof array.length === "number" ) {
        return true;
    }
    return false;
}
@mtod:为什么不应该对类型名称进行硬编码?毕竟,的返回值typeof是标准化的?
2021-03-16 22:13:07
第二个测试也会为字符串返回 true: typeof "abc".length === "number" // true
2021-03-20 22:13:07
另外,我想你永远不应该对类型名称进行硬编码,例如“数字”。尝试将其与实际数字进行比较,例如 typeof(obj) == typeof(42)
2021-04-08 22:13:07