Дифференциация между массивами и "хэшами" в Javascript

Чтобы сделать синтаксис для одной из моих функций более приятной, мне нужно определить, является ли конкретный параметр массивом или "хешем" (который я знаю как просто объекты).

Typeof не работает, потому что оба возвращают одно и то же

typeof {foo:"bar"} // Object

typeof ["foo","bar"] // Object

Итак, как бы я разделил эти два?

Я знаю, что это работает, но я надеюсь, что там будет лучше.

({foo:"bar"}).constructor // Object()

(["foo","bar"]).constructor // [ undefined ]

ИЗМЕНИТЬ Ах, кажется, [ undefined] в firebug - это то же самое, что и Array. Вид странный.

Ответ 1

Вы можете проверить свойство length как предложение SLaks, но как только вы передадите ему объект функции, вы будете удивлены, потому что на самом деле оно имеет свойство length. Также, если объект имеет определенное свойство длины, вы снова получите неправильный результат.

Ваш лучший выбор, вероятно:

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

jQuery использует его, а "пара" других людей...:)

Это более стойкое доказательство, чем метод instanceof. Этот метод также предлагается в следующей статье:

'instanceof' считается вредным (или как писать надежный 'isArray') (@kagax)

Другое дело добавить, что эта функция почти идентична функции Array.isArray в спецификации ES 5:

15.4.3.2 Array.isArray(arg)

  • Если Type (arg) не Object, верните ложь.
  • Если значение [[Класс]] внутреннее свойство arg "Массив" , затем верните true.
  • Возвращает false.

Ответ 2

something instanceof Array отлично работает в пределах одного документа, но не будет работать, если вы начнете передавать массивы между разными окнами, потому что Array из одного окна - это другой объект из Array на другой. Если у вас нет намерения делать кросс-окна-скрипты (и в целом это стоит избегать), я бы рекомендовал придерживаться этого.

Если вам нужна поддержка кросс-окон, все немного сложнее. В будущем рассказ прост, так как ECMAScript Fifth Edition определяет функцию, которая выполняет именно это:

Array.isArray([1]);   // -> true

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

Если он недоступен, вам приходится полагаться на сериализацию Object#toString, которая является уродливой и слегка изворотливой. Несмотря на то, что в целом он будет работать с современными браузерами, есть мыслимые случаи, когда это может быть не так (в первую очередь, с объектами хоста).

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

if (!('isArray' in Array)) {
    Array.isArray= function(o) {
        return Object.prototype.toString.call(o)==='[object Array]';
    };
}

Что касается constructor, никогда не используйте его. Он недоступен везде, и он не делает то, что вы думаете. Использование его почти всегда является ошибкой.

Ответ 3

Я думаю, самый элегантный способ - просто использовать оператор instanceof:

if (myVar instanceof Array)
    doSomething();
Примеры

:

[] instanceof Array           // true
{} instanceof Array           // false
{length:100} instanceof Array // false
null instanceof Array         // false

Edit:

Помните, что при тестировании объекта из другого iFrame он не сработает (см. ответы @galambalazs и @bobince)

Ответ 4

Проверьте свойство length:

"length" in {foo:"bar"}   //false
"length" in ["foo","bar"] //true

Ответ 5

Эта функция typeof используется для исправления столкновений массива/объекта для оператора typeof и столкновения null/object. Однако он не работает через рамки или через окна. См. Источник более продвинутой версии, которая работает с ними.

function typeOf(value) {
    var s = typeof value;
    if (s === 'object') {
        if (value) {
            if (value instanceof Array) {
                s = 'array';
            }
        } else {
            s = 'null';
        }
    }
    return s;
}

Источник: Douglas Crockford Javascript site