Проверка того, является ли объект массивным

Есть ли способ проверить, является ли объект "похожим на массив", как для этих типов объектов:

  • Массивы (duh)
  • Типированные массивы (Uint8Array и т.д.), они вернут false, если используется Array.isArray
  • аргумент object
  • нодлистов *
  • Есть несколько других, которые я не могу придумать из рук

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

Ответ 1

Насколько я нашел в своих исследованиях по этой теме, у вас есть только пара вариантов:

  • Вы можете посмотреть только на свойство .length и принять любой объект, который, как представляется, имеет соответствующее свойство .length, которое не является чем-то другим, что вы знаете, которого вы должны устранить (например, функция).

  • Вы можете проверить определенные объекты, похожие на массив (HTMLCollection, nodeList), и предубеждение в пользу них.

Вот два варианта для первого метода: тот, который не принимает нулевую длину и то, что делает (они включают предложения gilly3 и все, что мы видим в подобной функции jQuery):

// see if it looks and smells like an iterable object, but don't accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          item.hasOwnProperty("length") && 
          typeof item.length === "number" && 
          item.length > 0 && 
          (item.length - 1) in item
        )
    );
}

Это, конечно, сообщает false для элементов с .length === 0. Если вы хотите разрешить .length === 0, тогда логику можно включить и в этот случай.

// see if it looks and smells like an iterable object, and do accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          typeof (item.length) === "number" && 
          (item.length === 0 ||
             (item.length > 0 && 
             (item.length - 1) in item)
          )
        )
    );
}

Некоторые тестовые примеры: http://jsfiddle.net/jfriend00/3brjc/

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

Например, здесь метод, который я использую, когда хочу, чтобы я включил объекты nodeList и HTMLCollection, похожие на массив:

// assumes Array.isArray or a polyfill is available
function canAccessAsArray(item) {
    if (Array.isArray(item)) {
        return true;
    }
    // modern browser such as IE9 / firefox / chrome etc.
    var result = Object.prototype.toString.call(item);
    if (result === "[object HTMLCollection]" || result === "[object NodeList]") {
        return true;
    }
    //ie 6/7/8
    if (typeof item !== "object" || !item.hasOwnProperty("length") || item.length < 0) {
        return false;
    }
    // a false positive on an empty pseudo-array is OK because there won't be anything
    // to iterate so we allow anything with .length === 0 to pass the test
    if (item.length === 0) {
        return true;
    } else if (item[0] && item[0].nodeType) {
        return true;
    }
    return false;        
}

Ответ 2

Это дурацкий способ сделать это, но я протестировал и обнаружил, что он работает с NodeLists и массивами, но не с строками или цифрами.

myVar['somenoncollidingindex'] = true;
if(myVar.length !== undefined && myVar['somenoncollidingindex'] !== undefined) { /* hey, it array-ish! */ delete myVar['somenoncollidingindex']; }

Протестировано в движке firefox JS и в node.js(который основан на Chrome.)

Ответ 3

Ну, это зависит от того, что вы подразумеваете под массивом. Вероятно, вы могли бы перебирать цикл for следующим образом:

for (var i=0, l=obj.length; i<l; ++i) {
  var item = obj[i];
}

Итак, тест прост:

function isArrayLike(obj) {
  if (!obj) return false;
  var l = obj.length;
  if (typeof l != 'number' || l < 0) return false;
  if (Math.floor(l) != l) return false;
  // fast check
  if (l>0 && !((l-1) in obj)) return false;
  // more complete check (optional)
  for (var i=0; i<l; ++i) {
    if (!(i in obj)) return false;
  }
  return true;
}

Конечно, это не будет ловить массивы, которые малонаселены, но опять же, действительно ли они используются в качестве массивов? NodeLists и т.п. Не будут малозаселенными.

Наслаждайтесь!

Ответ 4

Я говорю, что ничто не сравнится с простотой и выразительностью расширения собственных объектов:

Object.prototype.isArrayLike         = function(){ return false; };
Array.prototype.isArrayLike          = function(){ return true; };
NodeList.prototype.isArrayLike       = function(){ return true; };
HTMLCollection.prototype.isArrayLike = function(){ return true; };

Этот подход может вызвать конфликты между фреймворками, однако я рекомендую сохранять дистанцию ​​от рамки, функция isArrayLike не соответствует тому, что предлагает название.

Ответ 5

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

isArrayLike = function (_) {

    _[0] = 0; return [].slice.call(_).length >= Object.values(_).length;
}

Это использует небольшой хак я случайно обнаружил, что позволяет определить, является ли объект (1) массив, (2) массив типа, или (3) объект/объект как.

Единственным недостатком является то, что он не работает правильно для массива-подобными, которые имеют объектно-подобные свойства добавлен, например, arguments

Ответ 6

Вы можете проверить, является ли объект итеративным:

function isIterable(o){
 return (o!=null && typeof(o[Symbol.iterator])==='function');
}

Осторожно, возвращает true для строк. Если это проблема, исключите их:

function isIterable(o){
 return (o!=null && typeof(o[Symbol.iterator])==='function' && typeof(o)!=='string');
}

Затем либо получите доступ к элементам, используя итератор, либо, если вы хотите использовать обычный массив [0], просто добавьте проверку длины.


Завершите функцию isArrayLike:

function isArrayLike(a){
 return (
  a!=null &&
  typeof(a[Symbol.iterator])==='function' &&
  typeof(a.length)==='number' &&
  typeof(a)!=='string'
 );
}

Ответ 7

Технически, (в значительной степени) каждый объект является "подобным массиву" (из-за приведения типа undefined) в соответствии со стандартом (спецификация языка ECMAScript 2015 §7.3.17, CreateListFromArrayLike (obj [, elementTypes] )):

7.3.17 CreateListFromArrayLike (obj [, elementTypes])

Абстрактная операция CreateListFromArrayLike используется для создания значения List, элементы которого предоставляются индексированными свойствами объекта, подобного массиву, obj. Необязательный аргумент elementTypes - это список, содержащий имена типов языка ECMAScript, которые разрешены для значений элементов создаваемого списка. Эта абстрактная операция выполняет следующие шаги:

  1. ReturnIfAbrupt (obj).
  2. Если elementTypes не был передан, пусть elementTypes будет (Undefined, Null, Boolean, String, Symbol, Number, Object).
  3. Если Type (obj) не является Object, генерировать исключение TypeError.
  4. Пусть len будет ToLength (Get (obj, "length")).
  5. ReturnIfAbrupt (len).
  6. Пусть список будет пустым списком.
  7. Пусть индекс будет 0.
  8. Повторите пока индекс <len
    1. Пусть indexName будет ToString (index).
    2. Пусть next будет Get (obj, indexName).
    3. ReturnIfAbrupt (следующий).
    4. Если Тип (рядом) не является элементом elementTypes, бросить исключение TypeError.
    5. Добавить следующий как последний элемент списка.
    6. Установите индекс на индекс + 1.
  9. Вернуться список

Сгенерировано через https://www.browserling.com/tools/html-to-markdown