JavaScript可迭代对象
可迭代对象(Iterable)是实现了[Symbol.Iterator]方法的对象。[Symbol.Iterator]方法应该返回一个迭代器(Iterator)。
JavaScript迭代器
迭代器(Iterator)是实现了next方法的对象。next方法会返回{ value: T, done: bool }结构的对象。T表示任意类型的对象。
可以把可迭代对象和迭代器合并为一个对象,也就是说同时实现[Symbol.Iterator]方法和next方法。
迭代器遍历语法
// makeRangeIterable的实现见下面
let iterable = makeRangeIterable(1, 10, 2);
for (let v of iterable) {
console.log(v);
}
上面的循环是个语法糖,等价于下面的语句:
for(let iterator = iterable[Symbol.iterator](), v = iterator.next();
!v.done ;
v = iterator.next()) {
console.log(v.value);
}
手工方法实现可迭代对象
函数实现可迭代对象
function makeRangeIterable(start = 0, end = Infinity, step = 1) {
const rangeIterable = {
[Symbol.iterator]: function() {
return {
nextIndex: start,
next: function() {
let result;
if (this.nextIndex < end) {
result = { value: this.nextIndex, done: false }
this.nextIndex += step;
return result;
}
return { done: true }
}
}
}
};
return rangeIterable;
}
类实现可迭代对象
class RangeIterator
{
nextIndex = 0;
end = Infinity;
step = 1;
constructor(start = 0, end = Infinity, step = 1)
{
this.nextIndex = start;
this.end = end;
this.step = step;
}
next() {
let result;
if (this.nextIndex < this.end) {
result = { value: this.nextIndex, done: false }
this.nextIndex += this.step;
return result;
}
return { done: true }
}
}
class RangeIterable
{
start = 0;
end = Infinity;
step = 1;
constructor(start = 0, end = Infinity, step = 1)
{
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
return new RangeIterator(this.start, this.end, this.step);
}
}
iterable = new RangeIterable(1, 6, 2);
可以看到使用类来实现可迭代对象清晰不少,对C++/Java程序员比较友好。
生成器实现可迭代对象
function* RangeIterableGenerator(start = 0, end = Infinity, step = 1)
{
for (let value = start; value < end; value += step)
yield value;
}
iterable = RangeIterableGenerator(1, 8, 2);
// 第一遍循环打印1,3,5,7
for (let v of iterable) console.log(v);
// 第二遍循环什么也不打印
for (let v of iterable) console.log(v);
可以看到生成器来实现可迭代对象多么容易。
上面的生成器等价于下面的类实现:
class RangeIterableAndIterator
{
start = 0;
end = Infinity;
step = 1;
nextIndex = 0;
constructor(start = 0, end = Infinity, step = 1)
{
this.start = start;
this.end = end;
this.step = step;
this.nextIndex = this.start;
}
[Symbol.iterator]() {
return this;
}
next() {
let result;
if (this.nextIndex < this.end) {
result = { value: this.nextIndex, done: false }
this.nextIndex += this.step;
return result;
}
return { done: true }
}
}
iterable = new RangeIterableAndIterator(1, 8, 2);
如果希望第二遍循环还是打印1,3,5,7, 删除构造函数里面的this.nextIndex = this.start;
把这句话添加到[Symbol.iterator]函数去。即:
[Symbol.iterator]() {
this.nextIndex = this.start;
return this;
}