Как определить, является ли переменная массивом
Каков наилучший стандартный метод перекрестного браузера для определения того, является ли переменная в JavaScript массивом или нет?
Поиск в Интернете есть несколько различных предложений, некоторые хорошие и немало недействительных.
Например, следующий базовый подход:
function isArray(obj) {
return (obj && obj.length);
}
Однако обратите внимание, что произойдет, если массив пуст, или obj на самом деле не является массивом, но реализует свойство length и т.д.
Итак, какая реализация является лучшей с точки зрения фактической работы, будучи кросс-браузером и по-прежнему эффективно работает?
Ответы
Ответ 1
Проверка типов объектов в JS выполняется через instanceof
, т.е.
obj instanceof Array
Это не будет работать, если объект передается через границы фрейма, поскольку каждый кадр имеет свой собственный объект Array
. Вы можете обойти это, проверив внутреннее свойство [[Class]] объекта. Чтобы получить его, используйте Object.prototype.toString()
(это гарантированно работает ECMA-262):
Object.prototype.toString.call(obj) === '[object Array]'
Оба метода будут работать только для реальных массивов, а не для подобных массиву объектов, таких как arguments
object или node. Поскольку все объекты типа массива должны иметь числовое свойство 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;
}
}
Ответ 2
Появление 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, если вы не уверены в решении, тогда я рекомендую вам протестировать его по душе и/или прочитать сообщение в блоге. Есть и другие потенциальные решения, если вам неудобно использовать условную компиляцию.
Ответ 3
У Крокфорда есть два ответа на стр. 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]');
};
Все обсуждения заставляют меня никогда не хотеть знать, является ли что-то массив.
Ответ 4
jQuery реализует функцию isArray, которая предлагает лучший способ сделать это:
function isArray( obj ) {
return toString.call(obj) === "[object Array]";
}
(фрагмент, взятый из jQuery v1.3.2 - слегка скорректированный, чтобы понять смысл контекста)
Ответ 5
Кража от гуру Джона Резига и jquery:
function isArray(array) {
if ( toString.call(array) === "[object Array]") {
return true;
} else if ( typeof array.length === "number" ) {
return true;
}
return false;
}
Ответ 6
Что вы собираетесь делать со значением, как только вы решите, что это массив?
Например, если вы собираетесь перечислять содержащиеся значения, если он выглядит как массив ИЛИ, если он является объектом, который используется в качестве хеш-таблицы, то следующий код получает то, что вы хотите (этот код останавливается, когда функция закрытия возвращает ничего, кроме "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" для нуль и undefined)
Примеры использования:
// Find first element who 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] );
}
})
}
Ответ 7
Если вы делаете это в CouchDB (SpiderMonkey), используйте
Array.isArray(array)
как array.constructor === Array
или array instanceof Array
не работают. Использование array.toString() === "[object Array]"
действительно работает, но кажется довольно хитроумным в сравнении.
Ответ 8
Если вам нужен кросс-браузер, вы хотите jQuery.isArray.
Ответ 9
В w3school есть пример, который должен быть довольно стандартным.
Чтобы проверить, является ли переменная массивом, они используют что-то похожее на это
function arrayCheck(obj) {
return obj && (obj.constructor==Array);
}
проверено на Chrome, Firefox, Safari, ie7
Ответ 10
Один из лучших исследуемых и обсуждаемых вариантов этой функции можно найти на сайте PHPJS. Вы можете ссылаться на пакеты, или вы можете напрямую перейти к функции . Я настоятельно рекомендую сайт для хорошо построенных эквивалентов функций PHP в JavaScript.
Ответ 11
Не хватает ссылок равным конструкторов. Иногда они имеют разные ссылки на конструктор. Поэтому я использую строковые представления из них.
function isArray(o) {
return o.constructor.toString() === [].constructor.toString();
}
Ответ 12
Замените Array.isArray(obj)
на obj.constructor==Array
образцы:
Array('44','55').constructor==Array
return true (IE8/Chrome)
'55'.constructor==Array
return false (IE8/Chrome)