Как проверить, является ли объект массивом?

2754

Я пытаюсь написать функцию, которая принимает список строк или одну строку. Если это строка, то я хочу преобразовать ее в массив только с одним элементом, чтобы я мог зацикливаться на ней, не опасаясь ошибки.

Так как же проверить, является ли переменная массивом?


Я собрал различные решения ниже и создал тест jsperf . Все они быстрые, так что просто используйте Array.isArray- теперь они хорошо поддерживаются и работают в разных кадрах .

mpen
источник
6
Я думал, что вы хотели «проверить, является ли объект массивом», но вы хотите проверить, является ли «объект массивом строк или отдельной строкой». Не уверены, что видите это? Или это только у меня так? Я думал о чем - то более , как это ... я один - то здесь отсутствует?
rr1g0
149
TL; DR - arr.constructor === Arrayсамый быстрый.
нету
3
jsben.ch/#/QgYAV - эталон для самых распространенных способов
EscapeNetscape
39
TL; DR - массив. isArray (arr) начиная с ES5; и $. isArray (arr) в jQuery.
Ондра Жижка
6
Просто имейте в виду, что если вы по какой-либо причине перезапишите свой конструктор с помощью прототипа, этот arr.constructor === Arrayтест вернет false. Array.isArray(arr)все еще возвращает истину, хотя.
ghaschel

Ответы:

979

В современных браузерах вы можете сделать

Array.isArray(obj)

( Поддерживается Chrome 5, Firefox 4.0, IE 9, Opera 10.5 и Safari 5)

Для обратной совместимости вы можете добавить следующее

# only implement if no native implementation is available
if (typeof Array.isArray === 'undefined') {
  Array.isArray = function(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
  }
};

Если вы используете JQuery, вы можете использовать jQuery.isArray(obj)или $.isArray(obj). Если вы используете подчеркивание, вы можете использовать_.isArray(obj)

Если вам не нужно обнаруживать массивы, созданные в разных кадрах, вы также можете просто использовать instanceof

obj instanceof Array
Фела Винкельмолен
источник
9
Вот более полный список браузеров, которые поддерживаютArray.isArray
lightswitch05
if (typeof Array.isArray === 'undefined') {может быть изменено наif(!Array.isArray) {
iMatoria
За то, что он стоит, Object.prototype.string.call(obj)может быть подделан, если на нем есть объект Symbol.toStringTag. Тем не менее, я не знаю ни о какой среде, которая отправляет, Symbol.toStringTagно не Array.isArrayтак, что это кажется безопасным.
Бенджамин Грюнбаум
5
Почему происходит instanceof Arrayсбой, если массив из другого фрейма?
NobleUplift
16
@NobleUplift: instanceof Arrayсбой, если массив из другого фрейма, потому что каждый массив из этого фрейма имеет свой Arrayконструктор и прототип. По соображениям совместимости / безопасности каждый кадр имеет свою собственную глобальную среду, включая глобальные объекты. ObjectГлобальный от одного кадра отличается от Objectглобальной от другого. Так же и для Arrayглобалов. Аксель Раушмайер больше говорит об этом .
Чи
1943

Метод, указанный в стандарте ECMAScript для поиска класса Object, заключается в использовании toStringметода из Object.prototype.

if( Object.prototype.toString.call( someVar ) === '[object Array]' ) {
    alert( 'Array!' );
}

Или вы можете использовать typeofдля проверки, если это строка:

if( typeof someVar === 'string' ) {
    someVar = [ someVar ];
}

Или, если вы не беспокоитесь о производительности, вы можете просто создать concatновый пустой массив.

someVar = [].concat( someVar );

Также есть конструктор, который вы можете запросить напрямую:

if (somevar.constructor.name == "Array") {
    // do something
}

Заканчивать тщательное лечение от @TJ Crowder в блоге, как размещено в комментариях ниже.

Проверьте этот тест, чтобы понять, какой метод работает лучше: http://jsben.ch/#/QgYAV

Из @Bharath преобразуйте строку в массив, используя Es6 для поставленного вопроса:

const convertStringToArray = (object) => {
   return (typeof object === 'string') ? Array(object) : object 
}

предположим:

let m = 'bla'
let n = ['bla','Meow']
let y = convertStringToArray(m)
let z = convertStringToArray(n)
console.log('check y: '+JSON.stringify(y)) . // check y: ['bla']
console.log('check y: '+JSON.stringify(z)) . // check y: ['bla','Meow']
user113716
источник
65
+1 Да, toStringэто один из способов пойти. Я делаю небольшую сводку
TJ Crowder
3
typeof new String('beans') > 'объект'
Бен
16
Если вы не хотите вводить «[object Array]», используйте Object.prototype.toString.call (someVar) === Object.prototype.toString.call ([]) или создайте вспомогательную функцию для получения типа, если вы не не хочу набирать Object.prototype.toString.call
Pramod
13
Я использую ванильный Array.isArray, который работает в «современных браузерах» (то есть IE9 + и всех остальных). А для поддержки старых браузеров используйте шайбу от MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Дэвид Гилбертсон,
21
Живи в современном миреArray.isArray(obj)
mcfedr
1274

Я бы сначала проверил, поддерживает ли ваша реализация isArray:

if (Array.isArray)
    return Array.isArray(v);

Вы также можете попробовать использовать instanceofоператор

v instanceof Array
ChaosPandion
источник
127
v instanceof Arrayвернет false, если vбыл создан в другом фрейме ( vэто экземпляр thatFrame.contentWindow.Arrayкласса).
pepkin88
47
Чтобы быть конкретным: Array.isArrayопределяется как часть ECMAScript 5 / Javascript 1.8.5.
Джевон
8
Действительно простое и аккуратное решение, НО isArray не совместим с некоторыми старыми браузерами (например, IE7 и IE8). Источник: kangax.github.io/es5-compat-table/#
Wookie88
2
Как насчет: if (Array.isArray) вернуть Array.isArray (v); иначе вернуть v instanceof Array;
lewdev
1
или просто:return (Array.isArray && Array.isArray(v)) || (v instanceof Array);
Стейн де Витт
298

JQuery также предлагает $.isArray()метод:

var a = ["A", "AA", "AAA"];

if($.isArray(a)) {
  alert("a is an array!");
} else {
  alert("a is not an array!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

janr
источник
27
Просто обратите внимание, jQuery использует метод toString для внутреннего использования: GitHub Source
Jacob Squires
5
@JacobSquires это зависит. Я только что проверил здесь, последний JQuery на Chrome - $.isArray === Array.isArrayвозвращает истину.
Ренан
3
@Renan: Это хорошая вещь об использовании jQuery. Обычно для этого используется самый современный и лучший метод, и вам не нужно выполнять все функции, проверяя себя, чтобы знать, что использовать.
благоговение
jQuery используется Array.isArrayза кулисами: github.com/jquery/jquery/blob/master/src/core.js#L211
Серджиу
1
Но никто не собирается использовать jQuery ПРОСТО ДЛЯ ЭТОЙ функциональности, верно? Я бы не просто загрузить JQuery , потому что я хочу , чтобы проверить , если что - то массив: р
Automagisch
106

Это самый быстрый из всех методов (поддерживаются все браузеры):

function isArray(obj){
    return !!obj && obj.constructor === Array;
}
шиноби
источник
2
Вы правы, это самый быстрый результат по тестам, которые я включил в вопрос.
mpen
5
Есть ли недостатки у этого метода? Это кажется намного более простым и эффективным, чем принятый, лучший ответ.
Дэвид Меза
@shinobi - просто любопытно (и я видел это часто) - почему вы формулируете условие, if (obj && Array === obj.constructor)а не if (obj && obj.constructor === Array)? Это потерянное в переводе на английский, а затем на код? Например, говорящие на английском языке обычно задают вопрос «существует ли объект и является ли он конструктором из класса массива?», поэтому поток кода при чтении более логичен. или есть какая-то техническая причина?
несинхронизировано
function object_type(o){var t = typeof(o);return ((t==="object") && (o.constructor===Array)) ? "array" : t;} /*allows you to */ switch(object_type(o)){ case 'array': break; case 'object' : o.dosomething();}
несинхронизировано
3
@shinobi все хорошо. я предполагаю, что это может быть похмелье от безопасной привычки c - если вы случайно используете = вместо ==, он не будет компилироваться, поскольку не является присваиваемой переменной.
несинхронизировано
47

Представьте, что у вас есть этот массив ниже :

var arr = [1,2,3,4,5];

Javascript (новые и старые браузеры):

function isArray(arr) {
  return arr.constructor.toString().indexOf("Array") > -1;
}

или

function isArray(arr) {
  return arr instanceof Array;
}

или

function isArray(arr) {
  return Object.prototype.toString.call(arr) === '[object Array]';
}

тогда назовите это так:

isArray(arr);

Javascript (IE9 +, Ch5 +, FF4 +, Saf5 +, Opera10.5 +)

Array.isArray(arr);

JQuery:

$.isArray(arr);

Угловой:

angular.isArray(arr);

Подчеркни и Лодаш:

_.isArray(arr);
Алиреза
источник
34

Array.isArray работает быстро, но поддерживается не всеми версиями браузеров. Таким образом, вы можете сделать исключение для других и использовать универсальный метод:

    Utils = {};    
    Utils.isArray = ('isArray' in Array) ? 
        Array.isArray : 
        function (value) {
            return Object.prototype.toString.call(value) === '[object Array]';
        }
CruorVult
источник
3
Вы должны получить .toString()метод от Object.prototype. Прямо сейчас вы используете то window.toString(), что не то же самое.
система
Вы правы. window.toStringсделать так же, как Object.prototype.toStringтолько в Chrome.
CruorVult
isArray совсем не быстр. Это самый медленный метод.
Джемилойи
Почему бы просто не добавить его в Array, а не в Utils? (Я знаю, что вам не нужны дополнительные свойства для новых объектов массива, но я думаю, что это произойдет, только если вы добавите isArray в Array.prototype.)
David Winiecki
27

Простая функция, чтобы проверить это:

function isArray(object)
{
    return object.constructor === Array;
}
MidnightTortoise
источник
16
Я бы сократил это до одной строки return object.constructor === Array- но вы уверены, что это вернет true только для массивов?
mpen
12
Может сделать это со всеми логическими выражениями. Меня if(x) return true; else return falseсводит с ума, когда я вижу :-) Даже если это задом наперед, вы должны отрицать выражение.
mpen
3
Причина, по которой это не возвращает true для getElementsByTagName, заключается в том, что результатом этой функции является на самом деле HTMLCollection, а не массив.
Ювал А.
6
Это плохо работает, если объект не определен или равен нулю.
Джон Хенкель
1
@JohnHenckel Смотрите мой ответ stackoverflow.com/a/29400289/34806, он учитывает как вашу озабоченность, так и самый первый комментарий, все в одной строке
Dexygen
17

Как MDN говорит здесь :

используйте Array.isArray или Object.prototype.toString.call, чтобы отличить обычные объекты от массивов

Нравится:

  • Object.prototype.toString.call(arr) === '[object Array]', или

  • Array.isArray(arr)

ajax333221
источник
17

Есть только одно решение для этого вопроса

x instanceof Array

где x - переменная, она вернет true, если x - массив, и false, если это не так.

Викаш Кумар
источник
Гораздо чище и безопаснее в будущем! Это или typeofсравнение.
ChristoKiwi
Это то, что получает хорошую поддержку браузера? Мне это нравится.
Дан Зузевич,
1
К сожалению, это на самом деле бесполезно без try / catch, потому что если «x» является объектом, таким как {}массив, вы получите синтаксическую ошибку.
августа
15

Вы можете проверить тип вашей переменной, является ли она массивом с;

var myArray=[];

if(myArray instanceof Array)
{
....
}
Ахмет Дал
источник
1
Несколько человек уже упомянули instanceof... Я думаю, что это терпит неудачу в нескольких странных сценариях.
mpen
15

Я бы сделал функцию для проверки типа объекта, с которым вы имеете дело ...

function whatAmI(me){ return Object.prototype.toString.call(me).split(/\W/)[2]; }

// tests
console.log(
  whatAmI(["aiming","@"]),
  whatAmI({living:4,breathing:4}),
  whatAmI(function(ing){ return ing+" to the global window" }),
  whatAmI("going to do with you?")
);

// output: Array Object Function String

тогда вы можете написать простое утверждение if ...

if(whatAmI(myVar) === "Array"){
    // do array stuff
} else { // could also check `if(whatAmI(myVar) === "String")` here to be sure
    // do string stuff
}
Билли Мун
источник
12

Я делаю это очень простым способом. Работает для меня. Есть ли недостатки?

Array.prototype.isArray = true;

a=[]; b={};
a.isArray  // true
b.isArray  // (undefined -> false)
rsbkk
источник
7
одураченный{isArray:true}
Берги
JSON.parse(someDataFromElsewhere).items.isArrayможет вернуть true (в зависимости от данных) и нарушить ваш код.
Рой Тинкер
12

Это моя попытка улучшить этот ответ с учетом комментариев:

var isArray = myArray && myArray.constructor === Array;

Он избавляется от if / else и учитывает возможность того, что массив будет нулевым или неопределенным

Dexygen
источник
конструктор недоступен в ES5
TechTurtle
11

Я обновил скрипку jsperf двумя альтернативными методами, а также проверкой ошибок.

Оказывается, что метод, определяющий постоянное значение в прототипах «Объект» и «Массив», работает быстрее, чем любой другой метод. Это несколько удивительный результат.

/* Initialisation */
Object.prototype.isArray = function() {
  return false;
};
Array.prototype.isArray = function() {
  return true;
};
Object.prototype._isArray = false;
Array.prototype._isArray = true;

var arr = ["1", "2"];
var noarr = "1";

/* Method 1 (function) */
if (arr.isArray()) document.write("arr is an array according to function<br/>");
if (!noarr.isArray()) document.write("noarr is not an array according to function<br/>");
/* Method 2 (value) - **** FASTEST ***** */
if (arr._isArray) document.write("arr is an array according to member value<br/>");
if (!noarr._isArray) document.write("noarr is not an array according to member value<br/>");

Эти два метода не работают, если переменная принимает неопределенное значение, но они работают, если вы уверены, что они имеют значение. Что касается проверки с учетом производительности, является ли значение массивом или единственным значением, второй метод выглядит как допустимый быстрый метод. Это немного быстрее, чем instanceof в Chrome, в два раза быстрее, чем второй лучший метод в Internet Explorer, Opera и Safari (на моей машине).

le_top
источник
9

Я знаю, что люди ищут какой-то грубый подход к JavaScript. Но если вы хотите меньше думать, посмотрите здесь: http://underscorejs.org/#isArray

_.isArray(object) 

Возвращает true, если объект является массивом.

(function(){ return _.isArray(arguments); })();
=> false
_.isArray([1,2,3]);
=> true
Евгений
источник
7
«Если не включен еще один тег для фреймворка / библиотеки, ожидается чистый ответ JavaScript».
Михал Перлаковский
5

Лучшее решение, которое я видел, это кросс-браузерная замена для typeof. Проверьте решение Ангуса Кролла здесь .

Ниже приведена версия TL; DR, но статья является отличным обсуждением этой проблемы, поэтому вам следует прочитать ее, если у вас есть время.

Object.toType = function(obj) {
    return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
}
// ... and usage:
Object.toType([1,2,3]); //"array" (all browsers)

// or to test...
var shouldBeAnArray = [1,2,3];
if(Object.toType(shouldBeAnArray) === 'array'){/* do stuff */};
Джон Вундес
источник
5

Вот мой ленивый подход:

if (Array.prototype.array_ === undefined) {
  Array.prototype.array_ = true;
}

// ...

var test = [],
    wat = {};

console.log(test.array_ === true); // true
console.log(wat.array_ === true);  // false

Я знаю, что кощунственно «связываться» с прототипом, но, похоже, он работает значительно лучше, чем рекомендуемый toStringметод .

Примечание: ловушка этого подхода заключается в том, что он не будет работать через iframeграницы , но для моего варианта использования это не проблема.

namuol
источник
с точки зрения производительности это уже не лучше, по крайней мере на FF30 на 64-битной Ubuntu
test30
2
одураченный wat = {array_: true}объектами.
Берги
@ Берги: Да, это должно быть очевидно. Если ты сидишь obj.array_ = true, то ты просто обманываешь себя .
namuol
@namuol: я не обязательно обманываю себя. Достаточно часто объекты используются в качестве словарей. Подумайте об cacheобъекте для запоминания результатов поиска, который использует строки поиска в качестве ключей свойств. Что делать, если пользователь ищет array_? Ваш объект становится массивом из-за этого? Это просто ошибка.
Берги
@namuol: Кроме того, этот подход требует, чтобы все вовлеченные стороны (включая используемые библиотеки) могли договориться о том, что .array_используется для маркировки массивов. Это действительно не тот случай, .arrayможет означать что угодно. Вы должны по крайней мере использовать описательную строку и сигнализировать о неприемлемости произвольного использования, например, с помощью .__isArray = true.
Берги
5

В книге Стояна Стефанова « Шаблоны JavaScript» есть хороший пример, который предполагает обработку всех возможных проблем, а также использует метод Array.isArray () ECMAScript 5 .

Итак, вот оно:

if (typeof Array.isArray === "undefined") {
    Array.isArray = function (arg) {
        return Object.prototype.toString.call(arg) === "[object Array]";
    };
}

Кстати, если вы используете jQuery, вы можете использовать его метод $ .isArray ()

Сальвадор Дали
источник
2
+1: почему не просто if(!Array.isArray) {...?
Марко Демайо
5

Самый простой и быстрый способ проверить, является ли объект массивом или нет.

 var arr = [];
  arr.constructor.name ==='Array'  //return true;

или

arr.constructor ===Array //return true;

или вы можете сделать служебную функцию:

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

Применение:

isArray(arr); //return true
sheelpriy
источник
5

Следующее может быть использовано, если вы знаете, что у вашего объекта нет метода concat.

var arr = [];
if (typeof arr.concat === 'function') {
    console.log("It's an array");
}

Есиль
источник
Это хороший трюк, но может быть переопределен ... но большую часть времени должны получить результат
Алирезу
5

Вы могли бы isArray метод, но я бы предпочел проверить с

Object.getPrototypeOf(yourvariable) === Array.prototype

СТАЛИ
источник
Почему вы предпочитаете это?
mpen
@mpen Object.getPrototypeOf(yourvariable)возвращает прототип объекта Array. И код является самым быстрым и безопасным, чтобы на него можно было положиться.
СТАЛЬ
1
Легко обмануть: pastebin.com/MP8d5bCE Кроме того, есть ли у вас тесты производительности, чтобы подтвердить свои «самые быстрые» претензии?
mpen
4

Если только два вида значений, которые могут быть переданы этой функции, являются строкой или массивом строк, сделайте это простым и используйте typeofпроверку на возможность строки:

function someFunc(arg) {
    var arr = (typeof arg == "string") ? [arg] : arg;
}
Тим Даун
источник
Да ... это сработало бы для этого сценария, но не в целом. Все равно закончилось использованием varargs. :)
mpen
4
A = [1,2,3]
console.log(A.map==[].map)

В поисках самой короткой версии вот что я получил до сих пор.

Обратите внимание, что не существует совершенной функции, которая всегда обнаруживает все возможные комбинации. Лучше знать все возможности и ограничения ваших инструментов, чем ожидать магического инструмента.

exebook
источник
1
небольшое происхождение от меня, A.map !== undefinedно да, это может быть скользкой дорогой в мире обезьяньих патчеров;)
dmi3y
К вашему сведению: это не работает в iFrames ( stackoverflow.com/questions/460256/… )
WiredPrairie
4
function isArray(value) {
    if (value) {
        if (typeof value === 'object') {
            return (Object.prototype.toString.call(value) == '[object Array]')
        }
    }
    return false;
}

var ar = ["ff","tt"]
alert(isArray(ar))
RoboTamer
источник
4

Простая функция для проверки, является ли входное значение массивом, выглядит следующим образом:

function isArray(value)
{
  return Object.prototype.toString.call(value) === '[object Array]';
}

Это работает кросс-браузер и со старыми браузерами. Это взято из сообщения в блоге TJ Crowders

Брэд Паркс
источник
4

Вы можете попробовать это:

var arr = []; (or) arr = new Array();
var obj = {}; (or) arr = new Object();

arr.constructor.prototype.hasOwnProperty('push') //true

obj.constructor.prototype.hasOwnProperty('push') // false
VIJAY P
источник
4

Эта функция превратит почти все в массив:

function arr(x) {
    if(x === null || x === undefined) {
        return [];
    }
    if(Array.isArray(x)) {
        return x;
    }
    if(isString(x) || isNumber(x)) {
        return [x];
    }
    if(x[Symbol.iterator] !== undefined || x.length !== undefined) {
        return Array.from(x);
    }
    return [x];
}

function isString(x) {
    return Object.prototype.toString.call(x) === "[object String]"
}

function isNumber(x) {
    return Object.prototype.toString.call(x) === "[object Number]"
}

Он использует некоторые новые функции браузера, поэтому вы можете захотеть заполнить его для максимальной поддержки.

Примеры:

> arr(null);
[]
> arr(undefined)
[]
> arr(3.14)
[ 3.14 ]
> arr(1/0)
[ Infinity ]
> gen = function*() { yield 1; yield 2; yield 3; }
[Function: gen]
> arr(gen())
[ 1, 2, 3 ]
> arr([4,5,6])
[ 4, 5, 6 ]
> arr("foo")
[ 'foo' ]

NB строки будут преобразованы в массив с одним элементом вместо массива символов. УдалитьisString чек, если вы предпочитаете его наоборот.

Я использовал Array.isArrayздесь, потому что это самый надежный и простой.

mpen
источник
4

В вашем случае вы можете использовать concatметод Array, который может принимать как отдельные объекты, так и массивы (и даже комбинированные):

function myFunc(stringOrArray)
{
  var arr = [].concat(stringOrArray);

  console.log(arr);

  arr.forEach(function(item, i)
  {
    console.log(i, "=", item);
  })
}

myFunc("one string");

myFunc(["one string", "second", "third"]);

concat кажется, один из самых старых методов Array (даже IE 5.5 хорошо это знает).

kolyaseg
источник