迭代器和生成器
迭代器
在 js 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。
一旦创建,迭代器对象可以通过重复的调用 next() 显式的迭代,迭代一个迭代器被成为消耗了这个迭代器,因为它通常只能执行一次,在产生终止值之后,对 next() 的额外调用应该继续返回 { done: true }。
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
let iterationCount = 0;
const rangeIterator = {
next: function() {
let result;
if(nextIndex < end) {
result = {
value: nextIndex,
done: false
}
nextIndex += step;
iterationCount ++;
return result;
}
}
}
return rangeIterator;
}
// 使用迭代器
let it = makeRangeIterator(1, 10, 2);
let result = it.next();
let bool = !result.done;
while(bool) {
result = it.next();
}
console.log(result.value)
生成器函数
虽然自定义的迭代器是一个有用的工具,但由于需要显示的维护其内部状态,因此需要谨慎的创建,
生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代器算法的函数,同时它可以自动维护自己的现状,
生成器函数使用 function * 语法编写,
最初调用时,生成器函数不执行任何代码,而是返回一种成为 Generator 的迭代器,通过调用生成器的下一个方法消耗值时,Generator 函数将执行,直到遇到 yield 关键字。
可以根据需要多次调用该函数,并且每次都返回一个新的 Generator,但每个 Generator 只能迭代一次。
// 和上面代码作用相同,
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
var a = makeRangeIterator(1, 10, 2);
console.log(a); // Generator { }
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
可迭代的对象
若一个对象拥有迭代行为,比如在 for…of 中会循环哪些值,那么那个对象便是一个可迭代对象,一些内置类型,如 Array 或 Map 拥有默认的迭代行为,而其他类型(如 Object)则没有。
为了实现可迭代,一个对象必须实现 @iterator 方法,这意味着这个对象(或者其原型链中的任意一个对象)必须具有一个带 Symbol.iterator 键(key)的属性。
自定义可迭代对象
var myIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
for(let value of myIterable) {
console.log(value); // 1 2 3
}
console.log([...myIterable]); // [ 1, 2, 3 ]
内置可迭代对象
String、Array、TypedArray、Map、Set 都是内置可迭代对象,因为它们的原型对象都拥有一个 Symbol.iterator 方法。
用于可迭代对象的语法
一些语句和表达式专用于可迭代对象,例如 for-of 循环、展开语法、yield* 和 解构赋值。
for(let value of ['a', 'b', 'c']) {
console.log(value); // a b c
}
// for...of 遍历对象会报错,只能遍历可迭代的对象
for(let value in {x:1, y:2, z:3}) {
console.log(value)
}
console.log([...'abc']); // [ "a", "b", "c" ]
function * gen() {
yield *['a', 'b', 'c']
}
var g = gen();
console.log(g.next()); // { value: "a", done: false }
console.log(g.next()); // { value: "b", done: false }
console.log(g.next()); // { value: "c", done: false }
console.log(g.next()); // { value: undefined, done: true }
// Set(3) [ "a1", "b1", "c1" ]
[a, b, c] = new Set(['a1', 'b1', 'c1']);
console.log(a); // a1
高级生成器
next() 方法也接受一个参数用于修改生成器内部状态,传递给 next() 的参数值会被 yield 接收,要注意的是,传给第一个 next() 的值会被忽略。
斐波那契数列生成器
function * fibonacci() {
var fn1 = 0;
var fn2 = 1;
while(true) {
var current = fn1;
fn1 = fn2;
fn2 = current + fn1;
var reset = yield current;
console.log('reset', reset);
if(reset) {
fn1 = 0;
fn2 = 1
}
}
}
var sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next(true).value); // 0