Перевірка, чи щось ітерабельне


104

У документах MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

for...ofКонструкція описана , щоб мати можливість перебрати «Iterable» об'єкти. Але чи є хороший спосіб вирішити, чи є об'єкт ітерабельним?

Я намагався знайти загальні властивості для масивів, ітераторів та генераторів, але не зміг цього зробити.

Окрім того, як робити for ... ofпробний блок та перевіряти помилки типу, чи існує чистий спосіб цього зробити?


Напевно, як автор, ви знаєте, чи є ваш об’єкт ітерабельним?
andrewb

5
Об'єкт передається як аргумент, я не впевнений.
simonzack

1
Чому б не перевірити тип аргументу?
Джеймс Брукнер

2
@ andrew-buchan, Джеймс Брукнер: Перевірка типів може спрацювати, але якщо ви прочитаєте документи MDN, ви помітите, що на ній написано "схоже на масив". Я точно не знаю, що це означає, звідси і питання.
simonzack

1
wiki.ecmascript.org/doku.php?id=harmony:iterators заявляє, що " Об'єкт є ітерабельним, якщо у нього є iterator()метод. " Однак, оскільки це чернетка, лише чек може бути залежним від впровадження. Яке середовище ви використовуєте?
Бергі

Відповіді:


142

Правильний спосіб перевірити наявність ітерабельності такий:

function isIterable(obj) {
  // checks for null and undefined
  if (obj == null) {
    return false;
  }
  return typeof obj[Symbol.iterator] === 'function';
}

Чому це працює (ітерабельний протокол поглиблено): https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols

Оскільки ми говоримо про ... про, я припускаю, ми перебуваємо в думці ES6.

Крім того, не дивуйтеся, що ця функція повертається true якщо objє рядком, оскільки рядки повторюють їх символи.


14
або Symbol.iterator in Object(obj),.

14
Існує (принаймні) один виняток із використання оператора "in": string. Рядок є ітерабельним (у перерахунку на ... of), але ви не можете використовувати "in" на ньому. Якщо не це, я вважаю за краще використовувати "in", це виглядає, безумовно, приємніше.
Томаш Куліч

чи не повинно бути return typeof obj[Symbol.iterator] === 'function'? "Щоб бути ітерабельним, об'єкт повинен реалізувати метод @@ iterator" - він визначає метод
callum

Немає належної семантики для того, щоб obj [Symbol.iterator] був іншим, ніж (невизначена або) функція. Якщо хтось поставив наприклад String туди, це погано, і IMO добре, якщо код вийде з ладу якомога швидше.
Томаш Куліч

Чи typeof Object(obj)[Symbol.iterator] === 'function'працювали б у всіх випадках?
Крейг Гідні

26

Чому так багатослівний?

const isIterable = object =>
  object != null && typeof object[Symbol.iterator] === 'function'

57
Readability > Clevernessзавжди повертається true.
jfmercer

27
Ха-ха, зазвичай я скаржуюся на нечитабельний код, але насправді я думаю, що це досить читабельно. Як і англійське речення: Якщо об’єкт не є нульовим, а властивість символьного ітератора є функцією, то воно є ітерабельним. Якщо це не просто мертво, я не знаю, що таке ...
adius

4
imo, сенс "читабельності" полягає в розумінні того, що відбувається без фактичного читання
Дмитро Паржицький

2
@Alexander Mills Ваше поліпшення коду погіршило. 1. Як @jfmercer сказав Readability > Cleverness, так що скорочення змінної objectв oне допомагає нікому. 2. Порожній рядок ''є ітерабельним, тому він повинен повернутися true.
adius

1
Зрозумів, я зрозумів, що є причина, чому він користується!= null
Олександр Міллз

22

Найпростішим рішенням є насправді це:

function isIterable (value) {
  return Symbol.iterator in Object(value);
}

Objectбуде обгортати все, що не є об'єктом, в одному inоператорі , що дозволяє оператору працювати, навіть якщо вихідне значення не є Об'єктом. nullі undefinedперетворюються на порожні об'єкти, тому немає потреби у виявленні крайових регістрів, а рядки загортаються в об'єкти String, які є ітерабельними.


1
Ви використали неправильний символ. Це: Symbol.iterator.
Гіл

@Gil Ти абсолютно прав, ой! Я мав би мати скопійований тестований код, а не вводити його безпосередньо в допис.
Доміно

Створення нового об’єкта з Objectмарно витраченим для цього виду перевірки.
Рубен Верборг

Я не можу сказати, що точно знаю, як Objectфункція реалізована, але вона створює новий об'єкт лише тоді, коли він valueвже не є об'єктом. Я б очікував, що комбінація inі Object(...)бути чимось браузерними двигунами легко оптимізувати, на відміну від скажімо value !== undefined && value !== null && value[Symbol.iterator] && true. Плюс це надзвичайно читабельний, що мене хвилює.
Доміно

9

Як Зауваження, BEWARE про визначення Iterable . Якщо ви приїжджаєте з інших мов, ви очікуєте, що щось, що ви можете повторити, скажімо, forцикл є ітерабельним . Боюся, що це не так, коли ітерабельний означає щось, що реалізує протокол ітерації .

Щоб зробити речі зрозумілішими, всі приклади вище повертаються falseна цей об'єкт, {a: 1, b: 2}оскільки цей об'єкт не реалізує протокол ітерації. Таким чином, ви не зможете повторити його за допомогою, for...of але ви все ще можете з afor...in .

Тож якщо ви хочете уникнути болісних помилок, зробіть свій код більш конкретним, перейменувавши свій метод, як показано нижче:

/**
 * @param variable
 * @returns {boolean}
 */
const hasIterationProtocol = variable =>
    variable !== null && Symbol.iterator in Object(variable);

Ви не маєте сенсу. Як ви думаєте, чому це зірветься undefined?
adius

@adius Я думаю, що я неправильно припустив, що ти робив object !== nullу своїй відповіді, але ти робиш object != nullтак, що undefinedв цьому конкретному випадку це не порушується . Я відповідно оновив свою відповідь.
Франческо Касула

Добре, я бачу. Btw: Ваш код неправильний. hasIterationProtocol('')повинен повернутися true! Як щодо того, як ви видалите свій код і просто залиште розділ пояснень, який можна повторити, це єдине, що додає вашій відповіді реальну цінність / щось нове.
adius

1
Це повертається true, видаливши частину старої відповіді, я з’єднав обидві функції і забув про суворе порівняння. Тепер я повернув оригінальну відповідь, яка працювала чудово.
Франческо Касула

1
Я ніколи б не подумав використовувати Object(...), приємний вилов. Але в цьому випадку нульова перевірка не потрібна.
Доміно

2

Сьогодні, як уже було сказано, перевірити, чи objможна це просто зробити

obj != null && typeof obj[Symbol.iterator] === 'function' 

Історична відповідь (більше не дійсна)

The for..ofКонструкція є частиною ECMAScript 6 - е видання Мова специфікації проекту. Тож це могло змінитися до остаточної версії.

У цьому проекті ітерабельні об'єкти повинні мати функціюiterator властивості.

Ви можете перевірити, чи об’єкт є таким, як він подібний:

function isIterable(obj){
   if(obj === undefined || obj === null){
      return false;
   }
   return obj.iterator !== undefined;
}

7
Властивість ітератора була тимчасовою річчю firefox. Це не сумісно з ES6. дивіться: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Амір Арад

2

Для ітераторів асинхронізації слід перевірити "Symbol.asyncIterator" замість "Symbol.iterator":

async function* doSomething(i) {
    yield 1;
    yield 2;
}

let obj = doSomething();

console.log(typeof obj[Symbol.iterator] === 'function');      // false
console.log(typeof obj[Symbol.asyncIterator] === 'function'); // true

1

Якщо ви хочете перевірити, чи є змінною об'єкт ( {key: value}) або масив ( [value, value]), ви можете зробити це:

const isArray = function (a) {
    return Array.isArray(a);
};

const isObject = function (o) {
    return o === Object(o) && !isArray(o) && typeof o !== 'function';
};

function isIterable(variable) {
    return isArray(variable) || isObject(variable);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.