如何检测变量是不是为数组

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)

【讨论】:

你为什么要用一个可怕的函数替换正确的函数?

以上是关于如何检测变量是不是为数组的主要内容,如果未能解决你的问题,请参考以下文章

原生如何检测变量是不是是一个数组的几种方法

如何判断javascript中的变量是不是为数组?

如何检测变量是不是为字符串

PHP中判断变量值是不是为空的问题

PHP中检测一个变量是不是有设置的函数是啥

在php中检测一个变量是不是设置需要使用啥函数