Как определить, является ли переменная массивом - PullRequest
99 голосов
/ 29 июня 2009

Каков лучший де-факто стандартный кросс-браузерный метод для определения, является ли переменная в JavaScript массивом или нет?

При поиске в Интернете есть несколько разных предложений, некоторые из которых хороши, а многие недействительны.

Например, следующий подход является базовым:

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

Однако обратите внимание, что происходит, если массив пуст или объект obj фактически не является массивом, но реализует свойство длины и т. Д.

Какая реализация является лучшей с точки зрения фактической работы, кросс-браузерности и эффективности работы?

Ответы [ 12 ]

157 голосов
/ 29 июня 2009

Проверка типов объектов в 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;
    }
}
38 голосов
/ 27 октября 2010

Появление ECMAScript 5th Edition дает нам наиболее надежный метод тестирования, если переменная является массивом, 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, если вы не уверены в решении, тогда я рекомендую вам протестировать его по душе и / или прочитать сообщение в блоге. Есть и другие потенциальные решения, если вам неудобно использовать условную компиляцию.

8 голосов
/ 29 июня 2009

У Крокфорда есть два ответа на странице 106 «Хороших частей». Первый проверяет конструктор, но выдаст ложные негативы в разных фреймах или окнах. Вот второй:

if (my_value && typeof my_value === 'object' &&
        typeof my_value.length === 'number' &&
        !(my_value.propertyIsEnumerable('length')) {
    // my_value is truly an array!
}

Крокфорд указывает, что эта версия будет идентифицировать массив arguments как массив, даже если у него нет ни одного из методов массива.

Его интересное обсуждение проблемы начинается на странице 105.

Существует еще одно интересное обсуждение (post-Good Parts) здесь , которое включает это предложение:

var isArray = function (o) {
    return (o instanceof Array) ||
        (Object.prototype.toString.apply(o) === '[object Array]');
};

Все обсуждения заставляют меня никогда не знать, является ли что-то массивом или нет.

2 голосов
/ 29 июня 2009

Кража у гуру Джона Резига и jquery:

function isArray(array) {
    if ( toString.call(array) === "[object Array]") {
        return true;
    } else if ( typeof array.length === "number" ) {
        return true;
    }
    return false;
}
2 голосов
/ 29 июня 2009

jQuery реализует функцию isArray, которая предлагает лучший способ сделать это:

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

(фрагмент взят из jQuery v1.3.2 - немного скорректирован, чтобы иметь смысл вне контекста)

1 голос
/ 20 ноября 2017

Если вы делаете это в CouchDB (SpiderMonkey), тогда используйте

Array.isArray(array)

как array.constructor === Array или array instanceof Array не работают. Использование array.toString() === "[object Array]" работает, но выглядит довольно хитроумно по сравнению.

1 голос
/ 29 июня 2009

Что вы собираетесь делать со значением, если решите, что это массив?

Например, если вы намереваетесь перечислить содержащиеся в нем значения, если оно выглядит как массив ИЛИ, если это объект, используемый в качестве хеш-таблицы, то следующий код получает то, что вы хотите (это код останавливается, когда функция закрытия возвращает что-либо, кроме «undefined». Обратите внимание, что она НЕ перебирает 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] );
        }
    })
}
0 голосов
/ 03 марта 2015

Заменить Array.isArray(obj) на obj.constructor==Array

образцы:

Array('44','55').constructor==Array вернуть true (IE8 / Chrome)

'55'.constructor==Array возврат false (IE8 / Chrome)

0 голосов
/ 29 июня 2009

Недостаточно ссылки, равной конструкторам. Иногда они имеют разные ссылки конструктора. Поэтому я использую их строковые представления.

function isArray(o) {
    return o.constructor.toString() === [].constructor.toString();
}
0 голосов
/ 29 июня 2009

На w3school есть пример, который должен быть вполне стандартным.

Чтобы проверить, является ли переменная массивом, они используют что-то похожее на это

function arrayCheck(obj) { 
    return obj && (obj.constructor==Array);
}

протестировано на Chrome, Firefox, Safari, т.е. 7

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...