迭代器和生成器
https://segmentfault.com/a/1190000010747122
写的足够好
迭代器模式
把有些结构成为可迭代对象(iterable),因为它们实现了正式的iterable接口,而且可以通过迭代器iterator消费
迭代器是按需创建的一次性对象。每个迭代器都会关联一个可迭代对象,而迭代器会暴露迭代其关联可迭代对象的API。迭代器无序了解与其关联的可迭代对象的结构,只需要知道如何取得连续的值。
可迭代协议
实现iterable接口(可迭代协议)要求具备的两种能力:
支持迭代的自我识别能力和创建实现iterator接口的对象的能力。这意味着必须暴露一个属性作为默认迭代器,而这个属性必须使用特殊的Symbol.iterator作为键。这个默认迭代器属性必须引用一个迭代器工厂函数,调用这个工厂函数必须返回一个新迭代器
Array 、 string \ set \ map \ argumengs\nodelist等均是可迭代
let num = 1;
console.log(num[Symbol.iterator])//undefined
这种方式可以检测是否迭代
接受可迭代对象的原生语言特性:
for of
数组结构
扩展操作符
Array.from()
创建集合
创建映射
Promise.all()
Promise.race()
yield*
如果对象原型链上的父类实现了iterable接口,那么这个对象也实现这个接口
class Foo extends Array {}
let foo = new Foo('foo','bar')
for(let el of foo) {
}
迭代器协议
迭代器是一种一次性使用的对象,迭代与其关联的可迭代对象。迭代器API使用next()方法遍历数据,每次返回一个IteratorResult对象,其中包含迭代器的下一个值
迭代器不知道可迭代对象多大
提前终止迭代器
return()必须返回一个有效的IteratorResult对象,可以只返回{done: true}
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let count = 1,
limit = this.limit;
return {
next() {
if(count <= limit) {
return {done: false, value: count++}
} else {
return {done: true};
}
},
return() {
console.log('exit early')
return {done: true}
}
}
}
}
let counter1 = new Counter(5)
for(let i of counter1) {
if(i > 2) {
break;
}
console.log(i);
}
输出
1
2
exit early
只要break就exit early,与console无关
break countinue return throw 结构操作
均可以实现提前退出
例如
let [a,b] = [1,2,3]
return()并不强制迭代器进入关闭状态,所以可以继续使用迭代器
let a = [1,2,3,4,5]
let iter = a[Symbol.iterator]();
iter.return = function () {
console.log('exit')
return {done: true}
}
for(let i of iter) {
console.log(i)
if(i>2)break;
}
for(let i of iter) {
console.log(i)
}
IteratorResult对象包含两个属性done 和 value
生成器
生成器拥有在一个函数块内暂停和恢复代码执行的能力。
生成器基础
调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行(suspended)状态。生成器对象也实现iterator接口,具有next()方法。
next()返回值有一个done和value属性
yield
上面链接对基础内容介绍已经足够
yield只能在生成器函数内部使用
function* valid() {
yield;//ok
}
function* invalid() {
function a() {
yield;//无效
}
}
生成器显示调用next()用处不大,但可以把生成器对象当做可迭代对象
function *gene() {
yield 1;
yield 2;
yield 3;
}
for(const x of gene()) console.log(x)
星号可以增强yield,使它能够迭代一个可迭代对象,从而一次产出一个值
yield*的值是关联迭代器返回done:true时的value属性。对于普通迭代器来说,这个值是undefined
function* gener() {
console.log('iter:',yield* [1,2,3])
}
for(const x of gener()) {
console.log('value:' ,x )
}
输出
value: 1
value: 2
value: 3
iter: undefined
对于生成器函数产生的迭代器,这个值就是生成器函数返回的值
function *inner() {
yield 'foo';
return 'bar'
}
function *outer(genObj) {
console.log('iter',yield *inner())
}
for(const x of outer()) {
console.log('value:',x)
}
输出
value: foo
iter bar
yield*实现递归
function *nTimes(n) {
if(n > 0) {
yield * nTimes(n - 1)
yield n - 1;
}
}
for (const x of nTimes(3) ) {
console.log(x)
}
提前终止生成器
return() throw() 都可以提前终止生成器
二者可以用于强制生成器进入关闭状态
function* gener() {
for(const x of [1, 2, 3]) {
yield x;
}
}
const g = gener();
console.log(g)
console.log(g.return(4))
console.log(g)
所有生成器都有return()方法,通过它进入关闭状态,就无法恢复了
function *gener() {
for (const x of [1, 2, 3]) {
yield x;
}
}
const g = gener();
for (const x of g) {
if (x > 1) {
g.return(4)
}
console.log(x)
}
输出
1
2
throw()
throw()方法会在暂停的时候将一个提供的错误注入到生成器对象中。如果错误未被处理,生成器就会关闭。
如果错误在生成器函数内部处理了,就不会关闭,还可以恢复执行。错误处理会跳过对应的yield