检查某些内容是否可迭代

sim*_*ack 85 javascript

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

for...of构造被描述为能够迭代"可迭代"对象.但有没有一种很好的方法来决定一个对象是否可迭代?

我试图找到数组,迭代器和生成器的公共属性,但一直无法这样做.

除了for ... of在try块中执行并检查类型错误之外,还有一种干净的方法吗?

Tom*_*ich 109

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

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

为什么会这样(深度可迭代协议):https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols

因为我们正在讨论for..of,我认为,我们处于ES6心态.

此外,true如果obj是一个字符串,这个函数返回时不要感到惊讶,因为字符串会迭代它们的字符.

  • 使用'in'运算符有一个(至少)一个例外:string.字符串是可迭代的(就for..of而言)但你不能在其上使用'in'.如果不是这样,我更喜欢使用'in'它看起来肯定更好. (12认同)
  • 或者,`Object(obj)中的`Symbol.iterator`. (10认同)
  • 不应该是“return typeof obj[Symbol.iterator] === 'function'”吗?“为了可迭代,对象必须实现 @@iterator 方法”——它指定方法 (2认同)

adi*_*ius 22

为什么这么冗长?

const isIterable = object =>
  object != null && typeof object[Symbol.iterator] === 'function'
Run Code Online (Sandbox Code Playgroud)

  • `Readability> Cleverness`总是返回`true`. (41认同)
  • 哈哈,通常我是那个抱怨不可读的代码的人,但实际上我觉得这很可读.就像一个英语句子:如果对象不是null并且符号迭代器属性是一个函数,那么它是可迭代的.如果那不简单,我不知道是什么...... (14认同)
  • 也许,您唯一需要阅读的就是名称:`isIterable`。 (3认同)
  • imo,"可读性"的要点是要了解在没有_actual_阅读的情况下发生了什么 (2认同)
  • @Alexander Mills您的改进使代码变得更糟。1.就像@jfmercer所说的“可读性>聪明”一样,将变量object简化为o不会对任何人产生帮助。2.空字符串“''`是可迭代的,因此它必须返回“ true”。 (2认同)

dot*_*hen 22

2022答案

\n

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

\n

长话短说

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

长答案

\n

让我们定义一些变量:

\n
const someNumber = 42;\n42\n\nconst someArray = [1,2,3];\n(3) [1, 2, 3]\n\nconst someString = "Hello";\n"Hello, world!"\n\nconst someObject = {a:"A", b:"B"};\n{a: "A", b: "B"}\n
Run Code Online (Sandbox Code Playgroud)\n

测试可迭代性forEach()

\n

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

\n
someNumber.forEach(x=>console.log(x));\nVM1526:1 Uncaught TypeError: someNumber.forEach is not a function at <anonymous>:1:12\n\nsomeArray.forEach(x=>console.log(x));\nVM916:1 1\nVM916:1 2\nVM916:1 3\nundefined\n\nsomeString.forEach(x=>console.log(x));\nVM957:1 Uncaught TypeError: someString.forEach is not a function at <anonymous>:1:12\n\nsomeObject.forEach(x=>console.log(x));\nVM994:1 Uncaught TypeError: someObject.forEach is not a function at <anonymous>:1:12\n
Run Code Online (Sandbox Code Playgroud)\n

只有数组似乎可以使用 进行迭代forEach()

\n

测试可迭代性for..of

\n

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

\n
for (x of someNumber) { console.log(x); }\nVM21027:1 Uncaught TypeError: someNumber is not iterable at <anonymous>:1:11\n    \nfor (x of someArray) { console.log(x); }\nVM21047:1 1\nVM21047:1 2\nVM21047:1 3\nundefined\n\nfor (x of someString) { console.log(x); }\nVM21065:1 H\nVM21065:1 e\nVM21065:1 l\nVM21065:1 l\nVM21065:1 o\nundefined\n\nfor (x of someObject) { console.log(x); }\nVM21085:1 Uncaught TypeError: someObject is not iterable at <anonymous>:1:11\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x8b数组和字符串似乎可以使用 for..of 进行迭代,但对象则不然。Number 和 Object 都抛出了错误。

\n

测试可迭代性for..in

\n

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

\n
for (x in someNumber) { console.log(x); }\nundefined\n\nfor (x in someArray) { console.log(x); }\nVM20918:1 0\nVM20918:1 1\nVM20918:1 2\nundefined\n\nfor (x in someString) { console.log(x); }\nVM20945:1 0\nVM20945:1 1\nVM20945:1 2\nVM20945:1 3\nVM20945:1 4\nundefined\n\nfor (x in someObject) { console.log(x); }\nVM20972:1 a\nVM20972:1 b\nundefined\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x8b数组、字符串和对象似乎都可以使用for..in. 尽管它没有迭代,但 Number 没有抛出错误。

\n

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

\n


Dom*_*ino 12

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

function isIterable (value) {
  return Symbol.iterator in Object(value);
}
Run Code Online (Sandbox Code Playgroud)

Object会将不是对象的任何东西包装在一起,in即使原始值不是对象,也允许操作员进行工作。null并被undefined转换为空对象,因此无需进行边缘大小写检测,并且字符串被包装为可迭代的String对象。


Fra*_*ula 9

作为一个旁注,当心有关的定义迭代.如果你来自其他语言,你会期望你可以迭代一些东西,比如说for循环是可迭代的.我担心这不是这里的情况,其中iterable意味着实现迭代协议的东西.

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

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

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


Kam*_*rey 7

对于异步迭代器,您应该检查“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
Run Code Online (Sandbox Code Playgroud)


Zuh*_*aha 7

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

打字稿

function isIterable(x: unknown): boolean {
  return !!x?.[Symbol.iterator];
}
Run Code Online (Sandbox Code Playgroud)

或者作为箭头函数

const isIterable = (x: unknown): boolean => !!x?.[Symbol.iterator];
Run Code Online (Sandbox Code Playgroud)

JavaScript

function isIterable(x: unknown): boolean {
  return !!x?.[Symbol.iterator];
}
Run Code Online (Sandbox Code Playgroud)

例子

const isIterable = (x: unknown): boolean => !!x?.[Symbol.iterator];
Run Code Online (Sandbox Code Playgroud)