JavaScript中的迭代器和生成器详解(2)
function Range(low, high){
this.low = low;
this.high = high;
}
现在我们创建一个自定义迭代器,它返回一个包含范围内所有整数的序列。迭代器接口需要我们提供一个 next() 方法用来返回序列中的下一个元素或者是抛出 StopIteration 异常。
function RangeIterator(range){
this.range = range;
this.current = this.range.low;
}
RangeIterator.prototype.next = function(){
if (this.current > this.range.high)
throw StopIteration;
else
return this.current++;
};
我们的 RangeIterator 通过 range 实例来实例化,同时维持一个 current 属性来跟踪当前序列的位置。
最后,为了让 RangeIterator 可以和 Range 结合起来,我们需要为 Range 添加一个特殊的 __iterator__ 方法。当我们试图去迭代一个 Range 时,它将被调用,而且应该返回一个实现了迭代逻辑的 RangeIterator 实例。
Range.prototype.__iterator__ = function(){
return new RangeIterator(this);
};
完成我们的自定义迭代器后,我们就可以迭代一个范围实例:
var range = new Range(3, 5);
for (var i in range)
print(i); //输出 3,然后 4,然后 5
生成器:一种更好的方式来构建迭代器
虽然自定义的迭代器是一种很有用的工具,但是创建它们的时候要仔细规划,因为需要显式的维护它们的内部状态。
生成器提供了很强大的功能:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。
生成器是可以作为迭代器工厂的特殊函数。如果一个函数包含了一个或多个 yield 表达式,那么就称它为生成器(译者注:Node.js 还需要在函数名前加 * 来表示)。
注意:只有 HTML 中被包含在 <script type="application/javascript;version=1.7"> (或者更高版本)中的代码块才可以使用 yield 关键字。XUL (XML User Interface Language) 脚本标签不需要指定这个特殊的代码块也可以访问这些特性。
当一个生成器函数被调用时,函数体不会即刻执行,它会返回一个 generator-iterator 对象。每次调用 generator-iterator 的 next() 方法,函数体就会执行到下一个 yield 表达式,然后返回它的结果。当函数结束或者碰到 return 语句,一个 StopIteration 异常会被抛出。
用一个例子来更好的说明:
function simpleGenerator(){
yield "first";
yield "second";
yield "third";
for (var i = 0; i < 3; i++)
yield i;
}
var g = simpleGenerator();
print(g.next()); //输出 "first"
print(g.next()); //输出 "second"
print(g.next()); //输出 "third"
print(g.next()); //输出 0
print(g.next()); //输出 1
print(g.next()); //输出 2
print(g.next()); //抛出 StopIteration 异常