在 MDN 文档中:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
该for...of
构造被描述为能够迭代“可迭代”对象。但是有没有确定对象是否可迭代的好方法?
我试图找到数组、迭代器和生成器的通用属性,但一直没能做到。
除了for ... of
在 try 块中执行并检查类型错误之外,是否有一种干净的方法来执行此操作?
在 MDN 文档中:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
该for...of
构造被描述为能够迭代“可迭代”对象。但是有没有确定对象是否可迭代的好方法?
我试图找到数组、迭代器和生成器的通用属性,但一直没能做到。
除了for ... of
在 try 块中执行并检查类型错误之外,是否有一种干净的方法来执行此操作?
检查可迭代性的正确方法如下:
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
ifobj
是一个字符串,因为字符串会迭代它们的字符。
最简单的解决方案实际上是这样的:
function isIterable (value) {
return Symbol.iterator in Object(value);
}
Object
将把任何不是对象的东西包装在一起,in
即使原始值不是对象,也允许操作员工作。null
并undefined
变成空对象,因此不需要边缘情况检测,并且字符串被包装成可迭代的字符串对象。
为什么这么啰嗦?
const isIterable = object =>
object != null && typeof object[Symbol.iterator] === 'function'
作为旁注,请注意 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);
如果您问的是“foo
可迭代”,那么您可能来自一种语言(PHP、Python),该问题只有一个答案。在现代 Javascript 中,有不同类型的可迭代对象。因此,您必须根据要对变量执行的操作来检查迭代的能力。
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..in
or for..of
。但是 Javascript 开发人员必须意识到这三种方法之间的差异,以及每种方法的不同行为。
对于异步迭代器,您应该检查“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
如今,如前所述,测试是否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;
}
如果对象具有该属性Symbol.iterator
,则它是可迭代的。然后我们可以obj
像这样简单地检查是否是可迭代的
function isIterable(x: unknown): boolean {
return !!x?.[Symbol.iterator];
}
或作为箭头函数
const isIterable = (x: unknown): boolean => !!x?.[Symbol.iterator];
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
如果您想检查变量是对象 ( {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
。