灏天阁

迭代器和生成器

· Yin灏

迭代器

在 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

- Book Lists -