Как определить, является ли переменная массивом

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

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

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

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

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

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

 stpe29 июн. 2009 г., 16:45
Приведенный пример не предназначен для ответа на сам вопрос, а является просто примером того, как можно найти решение, которое часто не срабатывает в особых случаях (например, в этом, отсюда и «Однако, обратите внимание ...»).
 Christoph29 июн. 2009 г., 17:50
@James: в большинстве браузеров (исключая IE) строки похожи на массивы (т.е. возможен доступ через числовые индексы)
 Foreever22 окт. 2014 г., 05:46
 Claudiu29 июн. 2009 г., 19:08
косяк & APOS; считаю, что это так сложно сделать ...
 James Hugard29 июн. 2009 г., 16:00
Не вернет ли это значение true для строки?

Ответы на вопрос(12)

роверяет конструктор, но выдаст ложные негативы в разных фреймах или окнах. Вот второе:

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]&a,pos;);
};

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

 29 июн. 2009 г., 17:37
@ Кристоф - я добавил еще немного через редактирование. Увлекательная тема.
 29 июн. 2009 г., 17:23
в IE это сломается для строковых объектов и исключит строковые примитивы, которые похожи на массивы, кроме IE; проверка [[Class]] лучше, если вы хотите фактические массивы; если вам нужны объекты, похожие на массивы, проверка слишком ограничена

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

(Примечание. Тест & quot; o! = Null & quot; тестирует как 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] );
        }
    })
}
 29 июн. 2009 г., 19:29
@ Кристоф - Конечно, это так. Должна быть какая-то причина, чтобы решить, является ли что-то массивом: потому что вы хотите что-то сделать со значениями. Наиболее типичные вещи (по крайней мере в моем коде) - это отображение, фильтрация, поиск или иное преобразование данных в массиве. Вышеприведенная функция делает именно это: если переданная вещь имеет элементы, которые можно перебирать, она перебирает их. Если нет, то он ничего не делает и безвредно возвращает неопределенное.
 29 июн. 2009 г., 20:46
@Джеймс:for..in перебирает перечисляемые свойства объектов; Вы не должны использовать его с массивами, потому что: (1) он медленный; (2) не гарантируется сохранение порядка; (3) он будет включать любое пользовательское свойство, установленное в объекте или любом из его прототипов, поскольку ES3 не включает какой-либо способ установки атрибута DontEnum; Могут быть и другие проблемы, которые ускользнули от меня
 29 июн. 2009 г., 21:58
@Christoph - с другой стороны, использование for (;;) не будет работать должным образом для разреженных массивов и не будет повторять свойства объекта. № 3 считается плохой формой для объекта по причине, которую вы упомянули. С другой стороны, вы настолько правы в отношении производительности: for..in на 36 раз медленнее, чем for (;;) в массиве элементов размером 1M. Вот это да. К сожалению, не применимо к нашему основному варианту использования, который заключается в переборе свойств объекта (хеш-таблиц).
 29 июн. 2009 г., 17:49
хотя ответ может быть интересным, на самом деле он не имеет ничего общего с вопросом (и, между прочим: перебирает массивы с помощьюfor..in это плохо [тм])
 29 июн. 2009 г., 19:29
@Christoph - Почему перебирает массивы с for..in bad [tm]? Как еще вы будете перебирать массивы и / или хеш-таблицы (объекты)?
Решение Вопроса

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;
    }
}
 29 июн. 2009 г., 20:32
@James: интересно - я не знал об этой утечке; в любом случае, это легко исправить: в IE только нативные объекты JS имеютhasOwnProperty метод, так что просто префикс вашегоinstanceof сobj.hasOwnProperty && ; Кроме того, это все еще проблема с IE7? мои простые тесты через диспетчер задач показывают, что память была восстановлена после сворачивания браузера ...
 25 окт. 2012 г., 00:56
@TravisJ: см.ECMA-262 5.1, section 15.2.4.2; внутренние имена классов по соглашению прописные - см.section 8.6.2
 04 янв. 2011 г., 22:05
+1, но этот ответ заставляет меня не любить Javascript еще больше.
 29 июн. 2009 г., 19:42
Начиная с MS JS 5.6 (IE6?), «Instanceof» Оператор вытек большой объем памяти при запуске с COM-объектом (ActiveXObject). Не проверял JS 5.7 или JS 5.8, но это все еще может сохраняться.
 29 июн. 2009 г., 18:11
Большое резюме о состоянии дел в проверке массива js.

и, является ли переменная массивом,Array.isArray():

Array.isArray([]); // true

В то время как принятый здесь ответ будет работать через фреймы и окна для большинства браузеров,it doesn't for Internet Explorer 7 and lower, так как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, если вы не уверены в этом решении, я рекомендую вам проверить его по своему вкусу и / или прочитать сообщение в блоге. Существуют и другие потенциальные решения, если вам неудобно использовать условную компиляцию.

 10 июл. 2011 г., 11:46
@Steffen: интересно, так что нативная реализацияisArray не возвращает истину из массивов, созданных в других режимах документа? Мне придется разобраться с этим, когда у меня будет время, но я думаю, что лучше всего подать ошибку в Connect, чтобы ее можно было исправить в IE 10.
 27 окт. 2010 г., 12:23
Вааааааа, я ненавижу IE. Я полностью забыл о различных «режимах документа» или «режимах шизо», как я их назову.
 27 окт. 2010 г., 11:47
@ Pumbaa80: Вы правы :-) IE8 исправляет проблему для своего собственного Object.prototype.toString, но при тестировании массива, созданного в режиме документа IE8, который передается в режим IE7 или более низкий режим документа, проблема сохраняется. Я только протестировал этот сценарий, а не наоборот. Я редактировал, чтобы применить это исправление только к IE7 и ниже.
 27 окт. 2010 г., 11:21
The accepted answer работает нормально в IE8 +, но не в IE6,7
 10 июл. 2011 г., 09:48
Хотя идеи великолепны и выглядят хорошо, это не работает для меня в IE9 с всплывающими окнами. Он возвращает ложь для массивов, созданных открывателем ... Существуют ли совместимые решения IE9? (Проблема, похоже, заключается в том, что IE9 реализует сам Array.isArray, который возвращает false для данного случая, когда это не должно быть.

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

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

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

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

 29 июн. 2009 г., 17:19
Почему ты так думаешь? О хрупких?
 29 июн. 2009 г., 19:26
Это ненадежно, потому что сам объект Array может быть перезаписан пользовательским объектом.
 29 июн. 2009 г., 17:39
Спасибо, нашел другое объяснение здесь:thinkweb2.com/projects/prototype/…
 29 июн. 2009 г., 17:32
@Kamarey:constructor является обычным свойством DontEnum объекта-прототипа; это может не быть проблемой для встроенных типов, если никто не делает глупостей, но для пользовательских типов это легко может быть; мой совет: всегда используйтеinstanceof, который проверяет цепочку прототипов и не полагается на свойства, которые могут быть произвольно перезаписаны
 29 июн. 2009 г., 16:52
с помощьюconstructor для проверки типа иммо слишком хрупкая; используйте вместо этого одну из предложенных альтернатив

function isArray(array) {
    if ( toString.call(array) === "[object Array]") {
        return true;
    } else if ( typeof array.length === "number" ) {
        return true;
    }
    return false;
}
 29 июн. 2009 г., 16:57
@mtod: почему имена типов не должны быть жестко закодированы? в конце концов, возвращаемые значенияtypeof стандартизированы?
 29 июн. 2009 г., 16:05
Второй тест также возвращает true для строки: typeof & quot; abc; .length === & quot; число & quot; // правда
 05 авг. 2011 г., 07:01
@ohnoes объяснись
 29 июн. 2009 г., 16:29
Кроме того, я полагаю, что вам никогда не следует указывать имена типов, например & quot; номер & quot ;. Попробуйте вместо этого сравнить его с фактическим числом, например typeof (obj) == typeof (42)
,

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

function isArray(o) {
    return o.constructor.toString() === [].constructor.toString();
}
 07 авг. 2015 г., 09:14
одураченный{constructor:{toString:function(){ return "function Array() { [native code] }"; }}}

которая предлагает лучший способ сделать это

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

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

 29 июн. 2009 г., 16:55
вы должны использоватьObject.prototype.toString() - это менее вероятно, что сломается
 29 июн. 2009 г., 16:00
Они возвращают false на IE (# 2968). (Из источника jquery)
 29 июн. 2009 г., 16:21
Этот комментарий в источнике jQuery, похоже, ссылается на функцию isFunction, а не на isArray

о найти наPHPJS сайт, Вы можете ссылаться на пакеты или вы можете перейти кфункционировать напрямую, Я настоятельно рекомендую сайт для хорошо сконструированных эквивалентов функций PHP в JavaScript.

Решение Вопроса

Array.isArray(obj) отobj.constructor==Array

образцы:

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

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

 07 авг. 2015 г., 09:11
Почему бы вам заменить правильную функцию на ужасную?

Array.isArray(array)

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

 26 окт. 2018 г., 21:01
Это работает в Node.js и в браузерах, а не только в CouchDB:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Ваш ответ на вопрос