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

97

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

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

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

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

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

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

возможный дубликатHow do you check if a variable is an array in JavaScript? Foreever
Приведенный пример не предназначен для ответа на сам вопрос, а является просто примером того, как можно найти решение, которое часто не срабатывает в особых случаях (например, в этом, отсюда и «Однако, обратите внимание ...»). stpe
косяк & APOS; считаю, что это так сложно сделать ... Claudiu
Не вернет ли это значение true для строки? James Hugard
@James: в большинстве браузеров (исключая IE) строки похожи на массивы (т.е. возможен доступ через числовые индексы) Christoph

Ваш Ответ

12   ответов
0
8

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

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

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

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

и, является ли переменная массивом,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, если вы не уверены в этом решении, я рекомендую вам проверить его по своему вкусу и / или прочитать сообщение в блоге. Существуют и другие потенциальные решения, если вам неудобно использовать условную компиляцию.

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

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

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

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

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

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

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

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? мои простые тесты через диспетчер задач показывают, что память была восстановлена после сворачивания браузера ...
@TravisJ: см.ECMA-262 5.1, section 15.2.4.2; внутренние имена классов по соглашению прописные - см.section 8.6.2
+1, но этот ответ заставляет меня не любить Javascript еще больше.
2

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

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

function isArray(o) {
    return o.constructor.toString() === [].constructor.toString();
}
одураченный{constructor:{toString:function(){ return "function Array() { [native code] }"; }}}
2

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

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

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

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

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

-4

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

образцы:

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

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

Почему бы вам заменить правильную функцию на ужасную?
1

Array.isArray(array)

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

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

Похожие вопросы