检查某事物是否可迭代

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

for...of结构被描述为能够在“可迭代”对象上进行迭代。但是有没有一种好的方法来判断一个对象是否是可迭代的呢?

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

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

78925 次浏览

现在,如前所述,要测试 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;
}

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

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的心态。

另外,如果 obj是字符串,当字符串迭代它们的字符时,这个函数返回 true也不要感到惊讶。

为什么这么啰嗦?

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

作为一个旁注,小心关于 可迭代的的定义。如果您来自其他语言,那么您可能希望使用 for循环进行迭代,比如 可迭代的循环。我恐怕这里的 可迭代的不是指实现 迭代协议的东西。

为了让事情更清楚,上面的所有例子都在这个对象 {a: 1, b: 2}上返回 false,因为这个对象没有实现迭代协议。所以你不能使用 for...of 但是来迭代它,你仍然可以使用 for...in

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

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


}

Object 将把任何不是对象的东西包装成一个对象。这可以防止 in操作符抛出异常,并消除检查边缘情况的需要。nullundefined变成空对象,而字符串被包装成可迭代的 字符串对象

我个人建议内联这段代码,而不是定义一个函数,因为它比调用某个 isIterable(x)函数要简短和明确。


请记住,可迭代的这个词特别指的是 可迭代协议,尽管存在不同的方法来循环对象。下列清单并非详尽无遗:

对于 异步迭代器,您应该检查“ Symbol.syncIterator”而不是“ 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

如果你想检查一个变量实际上是一个对象({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);
}

我正在寻找一张 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

如果对象具有属性 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

2022答案

如果您询问“ Is foo iterable”,那么您可能来自一种语言(PHP,Python) ,在这种语言中,这个问题只有一个答案。因此,必须根据对变量的处理方式检查迭代能力。

DR

  • 测试使用 forEach()!!foo.forEach进行迭代的能力,在 Array 上返回 true。
  • 测试使用 for..of!!foo[Symbol.iterator]进行迭代的能力,在 Array 或 String 上返回 true。
  • 测试使用 for..in!!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

只有 数组似乎可以用 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

数组和字符串似乎可以对 for 进行迭代。.但物体不是。数字和对象都抛出了一个错误。

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

数组、字符串和对象似乎都可以用 for..in迭代。虽然它没有迭代,但 Number 没有抛出错误。

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