JavaScript 支持通过数据集对象循环,例如使用控制结构的数组,如 for...of和扩散操作员 ...
. 这被称为可迭代,支持此功能的数据结构被称为可迭代。
Iterables 是提供一个机制的数据结构,允许其他数据用户以连续的方式公开访问其元素。
可迭代协议的概念可以被分为可迭代协议,数据结构本身,和迭代器,一个向导器移动的迭代器. 考虑一个数组,例如. 当数组被用在一个为...of
循环,可迭代属性被称为返回一个iterator
。 这个可迭代属性被命名为Symbol.iterator
,它返回的对象可以在一个共同的界面上使用,这是所有循环控制结构共享的。
在某种程度上,Symbol.iterator
可以与一个迭代器工厂相比,每当数据结构被放置在循环中时,它会产生一个迭代器。
随着迭代器在数据结构上移动并连续提供元素,由迭代器返回的对象包含一个值
和一个完成
属性。
该值表示 iterator 指定的当前数据值,完成
是一个 boolean 值,它告诉我们 iterator 是否已到达数据结构中的最后一个元素。
此 {值,完成}
被结构如循环所消耗,以便迭代器方法调用下一个对象,一个在 Symbol.iterator() 方法中定义的 next()
方法。
换句话说,迭代器属性可以定义为知道如何从集合中访问元素的属性,它还包含停止逻辑,例如当数组中不再有元素时。
了解对象和 iterables
默认情况下,JavaScript 对象不包含可重复性,这里有一些潜在的合理性:
- 对象的关键特征之一是它是用户定义的,所以在对象中沉默地滑动
[Symbol.iterator]()
可能会导致意想不到的行为. - 前一个点也意味着它可以由用户手动添加,考虑到所有对象组成可能并不相似。
如果您仍然需要一个可迭代的,对象上的简单可迭代的实现将看起来像这样:
1let Reptiles = {
2 biomes: {
3 water: ["Alligators", "Crocs"],
4 land: ["Snakes", "Turtles"]
5 },
6
7 [Symbol.iterator]() {
8 let reptilesByBiome = Object.values(this.biomes);
9 let reptileIndex = 0;
10 let biomeIndex = 0;
11 return {
12 next() {
13 if (reptileIndex >= reptilesByBiome[biomeIndex].length) {
14 biomeIndex++;
15 reptileIndex = 0;
16 }
17
18 if (biomeIndex >= reptilesByBiome.length) {
19 return { value: undefined, done: true };
20 }
21
22 return {
23 value: reptilesByBiome[biomeIndex][reptileIndex++],
24 done: false
25 };
26 }
27 };
28 }
29};
30
31// now iterate over the new `Reptiles` iterable:
32for (let reptile of Reptiles) console.log(reptile);
产量将是:
1Alligators
2Crocs
3Snakes
4Turtles
使用此示例,您可以看到可在对象中实现迭代器。Iterables 可以为对象提供强大的属性,在处理某些情况时提供易用性,并帮助我们避免写长路径名称。
进入Iterator
像「for...of」这样的循环具有内置机制来消耗迭代,直到「完成」值被评估为 true. 如果您想自行消耗迭代,但没有内置循环,您可以从迭代器中获取迭代器,然后手动拨打 next()。
考虑到与上面的相同的例子,你可以通过这样称呼它的Symbol.iterator
来获得从爬行动物
的迭代器:
1let reptileIterator = Reptiles[Symbol.iterator]();
您可以这样使用 iterator:
1console.log(reptileIterator.next());
2// {value: "Alligators", done: false}
3console.log(reptileIterator.next());
4// {value: "Crocs", done: false}
5console.log(reptileIterator.next());
6// {value: "Snakes", done: false}
7console.log(reptileIterator.next());
8// {value: "Turtles", done: false}
9console.log(reptileIterator.next());
10// {value: undefined, done: true}
11
12console.log(reptileIterator.next());
13// TypeError: Cannot read property 'length' of undefined
正如你所看到的,迭代器有一个next()
方法,它会返回迭代器中的下一个值。为完成
的值仅在最后一个值被返回后评估为真
,因此要通过整个迭代器,总会有一次再调用next()
,而不是迭代器中的数据。在迭代器到达迭代器的尽头之后,再次调用next()
会导致TypeError
被扔掉。
结论
通过本教程,您已经通过了通过JavaScript数据集循环背后的不同部分和机制,您已经通过了JavaScript对象不具有这种天生的迭代能力的原因,并展示了手动实现自己的迭代的可能性。