К счастью, 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 означает, что поддержка старых браузеров означает поощрение использования небезопасного программного обеспечения, а значит, подвергает пользователя риску вредоносного ПО.