检查是否可迭代


104

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

for...of构建体被描述为能够遍历“迭代”的对象。但是,有一种确定对象是否可迭代的好方法吗?

我试图找到数组,迭代器和生成器的通用属性,但是无法做到这一点。

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


当然,作为作者,您知道您的对象是否可迭代吗?
andrewb

5
我不确定该对象是作为参数传递的。
simonzack

1
为什么不测试参数的typeof?
James Bruckner 2013年

2
@ andrew-buchan,James Bruckner:检查类型可能有用,但是如果您阅读了MDN文档,您会注意到它说“类似数组”。我不知道这到底意味着什么,因此是问题。
simonzack 2013年

1
wiki.ecmascript.org/doku.php?id=harmony:iterators声明“ 如果对象具有iterator()方法,则该对象是可迭代的。 ”。但是,由于这是草稿,因此只有支票可能取决于实现。您使用什么环境?
Bergi 2013年

Answers:


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

因为我们在谈论for..of,所以我认为我们处于ES6思维定势中。

同样,true如果此函数返回的obj是字符串,也不要感到惊讶,因为字符串会遍历其字符。


14
Symbol.iterator in Object(obj)

14
使用(in)运算符(至少)有一个例外:字符串。字符串是可迭代的(就for..of而言),但不能在其上使用“ in”。如果不是这样,我宁愿使用“ in”,它看起来绝对更好。
Tomas Kulich 2015年

不是return typeof obj[Symbol.iterator] === 'function'吗?“为了可迭代,对象必须实现@@ iterator方法” –它指定方法
callum

obj [Symbol.iterator]没有(未定义或)函数以外的任何语义,都没有适当的语义。如果有人把String放在那儿,那是一件坏事,而IMO如果代码尽快失败则是一件好事。
Tomas Kulich

typeof Object(obj)[Symbol.iterator] === 'function'在所有情况下工作吗?
Craig Gidney

26

为什么这么冗长?

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

56
Readability > Cleverness总是返回true
jfmercer '16

26
哈哈,通常我是抱怨不可读代码的人,但实际上我认为这是很可读的。就像一个英语句子:如果对象不为null,并且符号迭代器属性是一个函数,则它是可迭代的。如果那还不简单,我不知道这是什么...
adius

4
imo,“可读性”的重点是在没有实际阅读的情况下了解发生的情况
Dmitry Parzhitsky

2
@Alexander Mills您的改进使代码变得更糟。1.就像@jfmercer说的那样Readability > Cleverness,因此缩短变量objecto对任何人都没有帮助。2.空字符串''是可迭代的,因此必须返回true
adius

1
知道了,我想这是有原因的!= null
Alexander Mills

22

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

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

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


1
您使用了错误的符号。是:Symbol.iterator
吉尔(Gil)

@吉尔,哎呀,您说得对!我应该有复制粘贴的测试代码,而不是直接在帖子中键入内容。
多米诺骨牌

使用新对象创建Object这种类型的检查很浪费。
Ruben Verborgh

我不能说我确切地知道该Object函数是如何实现的,但是它仅在value还没有对象的情况下才创建一个新对象。我希望in和的结合Object(...)可以使浏览器引擎轻松优化,而与say不同value !== undefined && value !== null && value[Symbol.iterator] && true。另外,我非常关心它的可读性。
Domino

9

作为一个旁注,当心有关的定义迭代。如果您来自其他语言,则希望可以迭代的内容(例如,for循环)是可迭代的。恐怕这里不是可迭代的意思是某种实现迭代协议的情况

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

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

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

你没有道理 您为什么认为它会破坏undefined
adius

@adius我认为我错误地以为您object !== null在回答时在做object != null,但是您在做,因此undefined在特定情况下并没有发生问题。我已经相应更新了我的答案。
Francesco Casula

好的我明白了。顺便说一句:您的代码不正确。hasIterationProtocol('')必须退货true!如何删除代码,只留下可迭代的解释部分,这是唯一增加真实价值的东西/答案中的新内容。
adius

1
true通过删除部分旧答案,它确实返回了,我合并了两个函数,却忘记了严格的比较。现在我放回原来的答案,效果很好。
Francesco Casula

1
我从来没有想过使用Object(...),很好的收获。但是在那种情况下,不需要空检查。
Domino

2

如今,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;
}

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.