Как проверить, является ли объект массивом? - PullRequest
2467 голосов
/ 23 января 2011

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

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


Я собрал различные решения ниже и создал jsperf test .

Ответы [ 44 ]

3 голосов
/ 19 июля 2018

К счастью, ECMA 5 представила Array.isArray() еще в декабре 2009 года. Если по какой-то причине вы используете версию JavaScript, более раннюю, чем ECMA 5, обновите ее.

Однако, если вы настаиваете на этом, то у массивов есть определенные свойства, которые отличают их от любого другого типа. Свойства, которые я не видел, упоминаются в других ответах. Давайте углубимся в политику JavaScript.

Массив - это объект (typeof [] === "object"), но, в отличие от традиционных объектов, они имеют свойство длины (typeof ( {} ).length === "undefined"). null также также объект (typeof null === "object"), но вы не можете получить доступ к свойству null, поскольку null является , а не объектом. Это ошибка в спецификации, которая восходит к самому началу JavaScript, когда объекты имели тег типа 0, а null был представлен в виде буквенного нулевого указателя 0x00, что приводило к путанице в интерпретаторе это с объектами.

К сожалению, это не учитывает [] против {length:0}. Итак, теперь мы должны обратиться к цепочке прототипов.

( [] ).__proto__ === Array.prototype && ( [] ).__proto__ !== Object.prototype.

Таким образом, без Array.isArray() это примерно самое близкое, что мы можем получить:

function is_array(array){
    return array !== null
        && typeof array === "object"
        && array.__proto__ === Array.prototype;
}

[ [], [1,2,3], {length: 0}, {},
  1, 0, Infinity, NaN, "1", "[1,2,3]",
  null, undefined, [null], [undefined], {a:[]},
  [{}], [{length: 0}], [Infinity], [NaN],
  {__proto__: Array.prototype}
].filter(is_array)
// Expected: [ [], [1,2,3], [null], [undefined], [{}], [{length: 0}], [Infinity], [NaN] ]
// Actual:   [ [], [1,2,3], [null], [undefined], [{}], [{length: 0}], [Infinity], [NaN], {__proto__: Array.prototype} ]

Объект, злонамеренно спроектированный так, чтобы выглядеть как массив, на самом деле проходит тест Тьюринга Однако замены цепочки прототипов цепочкой прототипов Array достаточно, чтобы она действовала как массив, фактически превращая ее в массив. Единственная вещь в мире, которая может сказать, что такой объект на самом деле не массив, это Array.isArray(). Но для целей, которые вы обычно проверяете, является ли объект массивом, указанный объект должен хорошо сочетаться с вашим кодом. Даже поведение при искусственном изменении длины массива одинаково: если длина больше, чем количество элементов в массиве, у вас будут «пустые слоты» этого специального «неявного неопределенного» типа, который каким-то образом отличается от не определено, а также === undefined; тот самый тип, который является причиной, по которой мы используем typeof obj !== "undefined", чтобы избежать выброса ReferenceError, потому что obj === undefined only не выдает ошибку, если obj было явно определено как undefined.

a = {__proto__: Array.prototype}; // Array {}
a.push(5)
a // [5]
a.length = 5
a // [5, empty x 4]
b = a.map(n => n*n) // [25, empty x 4]
b.push(undefined)
b.push(undefined)
b // [25, empty x 4, undefined, undefined]
b[1] // undefined
b[1] === b[5] // true
Array.isArray(a) // false
Array.isArray(b) // true

Не используйте is_array(), хотя. Одно дело изобретать велосипед в учебных целях. Это еще одна вещь, чтобы сделать это в производственном коде. Даже не используйте его как полифилл. Поддержка старых версий JS означает, что поддержка старых браузеров означает поощрение использования небезопасного программного обеспечения, а значит, подвергает пользователя риску вредоносного ПО.

2 голосов
/ 09 февраля 2019

Лучше всего сравнить его, используя constructor, что-то вроде этого

if(some_variable.constructor === Array){
  // do something
}

Вы также можете использовать другие методы, такие как typeOf, преобразовать его в строку и затем сравнить, но сравнить с dataType всегда лучше.

2 голосов
/ 12 октября 2018

Существуют и другие методы для проверки, но я предпочитаю следующий метод для проверки (так как вы можете легко проверять типы других объектов).

> a = [1, 2]
[ 1, 2 ]
>
> Object.prototype.toString.call(a).slice(8,).replace(/\]$/, '')
'Array'
>
> Object.prototype.toString.call([]).slice(8,-1) // best approach
'Array'

Объяснение (с простыми примерами на Node REPL)"

> o = {'ok': 1}
{ ok: 1 }
> a = [1, 2]
[ 1, 2 ]
> typeof o
'object'
> typeof a
'object'
>
> Object.prototype.toString.call(o)
'[object Object]'
> Object.prototype.toString.call(a)
'[object Array]'
>

объект или массив"

> Object.prototype.toString.call(o).slice(8,).replace(/\]$/, '')
'Object'
>
> Object.prototype.toString.call(a).slice(8,).replace(/\]$/, '')
'Array'
>

пусто или не определено "

> Object.prototype.toString.call(undefined).slice(8,).replace(/\]$/, '')
'Undefined'
> Object.prototype.toString.call(null).slice(8,).replace(/\]$/, '')
'Null'
>

String"

> Object.prototype.toString.call('ok').slice(8,).replace(/\]$/, '')
'String'

Number "

> Object.prototype.toString.call(19).slice(8,).replace(/\]$/, '')
'Number'
> Object.prototype.toString.call(19.0).slice(8,).replace(/\]$/, '')
'Number'
> Object.prototype.toString.call(19.7).slice(8,).replace(/\]$/, '')
'Number'
>

Я ценю предложение @mpen использовать -1 вместо регулярного выражения следующим образом.

> Object.prototype.toString.call(12).slice(8,-1)
'Number'
>
> Object.prototype.toString.call(12.0).slice(8,-1)
'Number'
>
> Object.prototype.toString.call([]).slice(8,-1)
'Array'
> Object.prototype.toString.call({}).slice(8,-1)
'Object'
>
> Object.prototype.toString.call('').slice(8,-1)
'String'
>
1 голос
/ 13 апреля 2015
var is_array = function (value) {
   return value &&
     typeof value === 'object' &&
     typeof value.length === 'number' &&
     typeof value.splice === 'function' &&
    !(value.propertyIsEnumerable('length'));
};

Эта функция взята из книги "JS the good parts", отлично подходит для меня.

0 голосов
/ 23 апреля 2019

Я знаю, что это старый вопрос, но сейчас я нашел самый короткий ответ:

var x = [1,2,3]
console.log(x.map?1:0)
0 голосов
/ 04 февраля 2019

Вот фрагмент кода, который объяснит важный факт массивов, которые должны быть известны на ранних этапах изучения JS (в отличие от меня).

// this functions puts a string inside an array
var stringInsideArray = function(input) {
  if (typeof input === 'string') {
    return [input];
  }
  else if (Array.isArray(input)) {
    return input;
  } 
  else {
    throw new Error("Input is not a string!");
  }
}

var output = stringInsideArray('hello');
console.log('step one output: ', output); // ["hello"]

// use typeof method to verify output is an object
console.log('step two output: ', typeof output); // object

// use Array.isArray() method to verify output is an array
console.log('step three output: ', Array.isArray(output)); // true

Массивы , фактически являются объектами.

Используя оператор typeof , вывод stringInsideArray('hello') доказывает, что ["hello"] является действительно объектом. Это сбило меня с толку дольше всего, потому что я предполагал, что массивы будут типом данных JavaScript ...

Существует только 7 типов данных JS, а массивы НЕ один из них.

Чтобы ответить на ваш вопрос, с помощью метода Array.isArray () определите, что output является массивом.

0 голосов
/ 12 февраля 2013

Поскольку мне не нравятся вызовы Object.prototype, я искал другое решение. Тем более, что решения ChaosPandion не всегда работают, а решение MidnightTortoise с isArray() не работает с массивами из DOM (например, getElementsByTagName ). И, наконец, я нашел простое и кросс-браузерное решение, которое, вероятно, также работало бы с Netscape 4.;)

Это всего лишь 4 строки (проверка любого объекта h):

function isArray(h){
    if((h.length!=undefined&&h[0]!=undefined)||(h.length===0&&h[0]===undefined)){
        return true;
    }
    else{ return false; }
}

Я уже тестировал эти массивы (все возвращают true):

1) array=d.getElementsByName('some_element'); //'some_element' can be a real or unreal element
2) array=[];
3) array=[10];
4) array=new Array();
5) array=new Array();
   array.push("whatever");

Кто-нибудь может подтвердить, что это работает для всех случаев? Или кто-нибудь находит случай, когда мое решение не работает?

0 голосов
/ 06 апреля 2015

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

function isArray (o) {
    return typeof o === "object" && o.length !== undefined;
}

isArray({}); // false
isArray(1); // false
isArray("str"); // false
isArray(function(){}); // false

isArray([]); // true

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

isArray({length:0}); // true

Если вы согласны с этим недостатком и знаете, что ваши чистые объекты не будут иметь этого свойства, это чистое решение и должно быть быстрее, чем Object.Метод prototype.toString.call.

0 голосов
/ 13 декабря 2018

есть разница между checkout, его прототипом и Array.isArray:

function isArray(obj){
    return Object.getPrototypeOf(obj) === Array.prototype
}

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

но для этого объекта Proxy:

var arr = [1,2,3]

var proxy = new Proxy(arr,{})

console.log(Array.isArray(proxy)) // true

Array.isArray примет это как массив.

0 голосов
/ 27 сентября 2018

Вы также можете проверить с помощью свойства длины массива.Когда вы попытаетесь получить доступ к свойству длины массива, он вернет число (0 для пустого массива), а если вы попытаетесь получить доступ к свойству длины объекта, он вернет undefined.

if(Object.prototype.toString.call(arrayList) === '[object Array]') {
  console.log('Array!');
}
...