138

在 MDN 文档中:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

for...of构造被描述为能够迭代“可迭代”对象。但是有没有确定对象是否可迭代的好方法?

我试图找到数组、迭代器和生成器的通用属性,但一直没能做到。

除了for ... of在 try 块中执行并检查类型错误之外,是否有一种干净的方法来执行此操作?

4

10 回答 10

191

检查可迭代性的正确方法如下:

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

既然我们在谈论 for..of,我假设,我们处于 ES6 思维模式。

另外,不要惊讶这个函数返回trueifobj是一个字符串,因为字符串会迭代它们的字符。

于 2015-09-12T12:37:13.343 回答
44

最简单的解决方案实际上是这样的:

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

Object将把任何不是对象的东西包装在一起,in即使原始值不是对象,也允许操作员工作。nullundefined变成空对象,因此不需要边缘情况检测,并且字符串被包装成可迭代的字符串对象。

于 2018-11-01T18:06:40.437 回答
30

为什么这么啰嗦?

const isIterable = object =>
  object != null && typeof object[Symbol.iterator] === 'function'
于 2016-06-15T14:09:01.200 回答
11

作为旁注,请注意 iterable定义。如果您来自其他语言,您会期望可以迭代的东西,例如循环是可迭代的。恐怕这里的情况并非如此iterable意味着实现了迭代协议的东西。for

为了让事情更清楚,上面false的所有示例都返回此对象{a: 1, b: 2},因为该对象没有实现迭代协议。因此,您将无法使用 a 对其进行迭代,for...of 您仍然可以使用for...in.

因此,如果您想避免痛苦的错误,请通过重命名您的方法来使您的代码更加具体,如下所示:

/**
 * @param variable
 * @returns {boolean}
 */
const hasIterationProtocol = variable =>
    variable !== null && Symbol.iterator in Object(variable);
于 2017-06-16T10:10:56.790 回答
8

2022答案

如果您问的是“foo可迭代”,那么您可能来自一种语言(PHP、Python),该问题只有一个答案。在现代 Javascript 中,有不同类型的可迭代对象。因此,您必须根据要对变量执行的操作来检查迭代的能力。

TL;博士

  • forEach()测试使用with进行迭代的能力!!foo.forEach,在数组上返回 true。
  • for..of测试使用with进行迭代的能力!!foo[Symbol.iterator],在 Array 或 String 上返回 true。
  • for..in测试使用with进行迭代的能力!!Object.keys(Object(foo)).length,在 Array、String 或 Object 上返回 true。

长答案

让我们定义一些变量:

const someNumber = 42;
42

const someArray = [1,2,3];
(3) [1, 2, 3]

const someString = "Hello";
"Hello, world!"

const someObject = {a:"A", b:"B"};
{a: "A", b: "B"}

测试可迭代性forEach()

哪些类型可以用 迭代forEach(),用 测试!!foo.forEach

someNumber.forEach(x=>console.log(x));
VM1526:1 Uncaught TypeError: someNumber.forEach is not a function at <anonymous>:1:12

someArray.forEach(x=>console.log(x));
VM916:1 1
VM916:1 2
VM916:1 3
undefined

someString.forEach(x=>console.log(x));
VM957:1 Uncaught TypeError: someString.forEach is not a function at <anonymous>:1:12

someObject.forEach(x=>console.log(x));
VM994:1 Uncaught TypeError: someObject.forEach is not a function at <anonymous>:1:12

只有Array似乎可以用forEach().

测试可迭代性for..of

哪些类型可以用 迭代for..of,用 测试!!foo[Symbol.iterator]

for (x of someNumber) { console.log(x); }
VM21027:1 Uncaught TypeError: someNumber is not iterable at <anonymous>:1:11
    
for (x of someArray) { console.log(x); }
VM21047:1 1
VM21047:1 2
VM21047:1 3
undefined

for (x of someString) { console.log(x); }
VM21065:1 H
VM21065:1 e
VM21065:1 l
VM21065:1 l
VM21065:1 o
undefined

for (x of someObject) { console.log(x); }
VM21085:1 Uncaught TypeError: someObject is not iterable at <anonymous>:1:11

Array 和 String似乎可以用for..of 进行迭代,但 Object 不是。Number 和 Object 都抛出了错误。

测试可迭代性for..in

哪些类型可以用 迭代for..in,用 测试!!Object.keys(Object(foo)).length

for (x in someNumber) { console.log(x); }
undefined

for (x in someArray) { console.log(x); }
VM20918:1 0
VM20918:1 1
VM20918:1 2
undefined

for (x in someString) { console.log(x); }
VM20945:1 0
VM20945:1 1
VM20945:1 2
VM20945:1 3
VM20945:1 4
undefined

for (x in someObject) { console.log(x); }
VM20972:1 a
VM20972:1 b
undefined

Array 、String 和 Object似乎都可以用for..in. 虽然它没有迭代,但 Number 没有抛出错误。

在现代的 ES6 Javascript 中,我看到forEach使用的频率远远超过for..inor for..of。但是 Javascript 开发人员必须意识到这三种方法之间的差异,以及每种方法的不同行为。

于 2021-10-17T12:12:49.383 回答
5

对于异步迭代器,您应该检查“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
于 2019-05-07T06:24:21.250 回答
4

如今,如前所述,测试是否obj可迭代只是做

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

历史答案(不再有效)

for..of结构是 ECMASCript 第 6 版语言规范草案的一部分。所以它可能会在最终版本之前改变。

在这个草案中,可迭代对象必须具有iterator作为属性的功能。

您可以像这样检查对象是否可迭代:

function isIterable(obj){
   if(obj === undefined || obj === null){
      return false;
   }
   return obj.iterator !== undefined;
}
于 2014-06-07T17:01:00.223 回答
2

如果对象具有该属性Symbol.iterator,则它是可迭代的。然后我们可以obj像这样简单地检查是否是可迭代的

打字稿

function isIterable(x: unknown): boolean {
  return !!x?.[Symbol.iterator];
}

或作为箭头函数

const isIterable = (x: unknown): boolean => !!x?.[Symbol.iterator];

JavaScript

const isIterable = x => !!x?.[Symbol.iterator];

例子

isIterable(["hello", "world"]); // true
isIterable({}); // false
isIterable(null); // false
isIterable(undefined); // false
isIterable(1); // false
isIterable(true); // false
isIterable(Symbol("foo")); // false
isIterable(new Set()); // true
isIterable(new Map()); // true
于 2021-08-12T12:17:12.923 回答
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);
}
于 2019-07-10T13:55:29.530 回答
0

我正在寻找支票for ... in并决定以下内容。

isIterable (value) {
  // add further checks here based on need.
  return Object.keys(Object(value)).length > 0
}

这将返回true任何可迭代且至少具有一个值的东西。因此,空字符串、空数组、空对象等将返回false. 但{a: 'x', b:'y'}会回来true

于 2020-10-20T17:28:22.803 回答