本文主要是介绍《每天十分钟》-红宝书第4版-迭代器与生成器(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
生成器
基础知识
生成器的形式是一个函数,这个函数比较特殊,它拥有在一个函数块内暂停和恢复代码执行的能力。
函数名称前面加一个星号(*) 这就表示它是一个生成器了
// 生成器函数声明
function* generatorFn() {}
// 生成器函数表达式
let generatorFn = function* () {}
// 作为对象字面量方法的生成器函数
let foo = { * generatorFn() {}
}
// 作为类实例方法的生成器函数
class Foo { * generatorFn() {}
}
// 作为类静态方法的生成器函数
class Bar { static * generatorFn() {}
}
标识生成器函数的星号不受两侧空格的影响
调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行(suspended)的状态。与
迭代器相似,生成器对象也实现了 Iterator 接口,因此具有 next()方法。调用这个方法会让生成器
开始或恢复执行。
function* generatorFn() {}
const g = generatorFn();
console.log(g); // generatorFn {<suspended>}
console.log(g.next); // f next() { [native code] }
// 函数体为空的生成器函数中间不会停留,调用一次 next()就会让生成器到达 done: true 状态
console.log(g.next()); // { done: true, value: undefined }
value 属性是生成器函数的返回值,默认值为 undefined,可以通过生成器函数的返回值指定:
function* generatorFn() {return 'foo';
}
let generatorObject = generatorFn();
console.log(generatorObject); // generatorFn {<suspended>}
console.log(generatorObject.next()); // { done: true, value: 'foo' }
通过 yield 中断执行
function* generatorFn() { yield 'foo'; yield 'bar'; return 'baz';
}
let generatorObject = generatorFn();
let generatorObject2 = generatorFn();
console.log(generatorObject.next()); // { done: false, value: 'foo' }
console.log(generatorObject.next()); // { done: false, value: 'bar' }
console.log(generatorObject.next()); // { done: true, value: 'baz' }
//生成器函数内部的执行流程会针对每个生成器对象区分作用域。在一个生成器对象上调用 next()不会影响其他生成器
console.log(generatorObject2.next()); // { done: false, value: 'foo' }
使用场景
在生成器对象上显式调用 next()方法的用处并不大。那使用生成器适用哪些场景
生成器对象作为可迭代对象
function* generatorFn() { yield 1; yield 2; yield 3;
}
// 这里就是遍历可迭代对象
for (const x of generatorFn()) { console.log(x);
}
// 1
// 2
// 3
实现输入输出
//因为函数必须对整个表达式求值才能确定要返回的值,所以它在遇到 yield 关键字时暂停执行并计算出要产生的值:"foo"。下一次调用 next()传入了"bar",作为交给同一个 yield 的值。然后这个值被确定为本次生成器函数要返回的值
function* generatorFn() { return yield 'foo';
}
let generatorObject = generatorFn();
console.log(generatorObject.next()); // { done: false, value: 'foo' }
console.log(generatorObject.next('bar')); // { done: true, value: 'bar' }
yield 关键字并非只能使用一次。比如,以下代码就定义了一个无穷计数生成器函数:
function* generatorFn() { for (let i = 0;;++i) { yield i; }
}
let generatorObject = generatorFn();
console.log(generatorObject.next().value); // 0
console.log(generatorObject.next().value); // 1
console.log(generatorObject.next().value); // 2
console.log(generatorObject.next().value); // 3
console.log(generatorObject.next().value); // 4
console.log(generatorObject.next().value); // 5
有限计数
function* nTimes(n) { for (let i = 0; i < n; ++i) { yield i; }
}
for (let x of nTimes(3)) { console.log(x);
}
// 0
// 1
// 2
产生可迭代对象
使用星号增强 yield
// 等价的 generatorFn:
// function* generatorFn() {
// for (const x of [1, 2, 3]) {
// yield x;
// }
// }
function* generatorFn() { yield* [1, 2, 3];
}
let generatorObject = generatorFn();
for (const x of generatorFn()) { console.log(x);
}
// 1
// 2
// 3
因为 yield*实际上只是将一个可迭代对象序列化为一连串可以单独产出的值,所以这跟把 yield放到一个循环里没什么不同
使用 yield*实现递归算法
function* nTimes(n) { if (n > 0) { yield* nTimes(n - 1); yield n - 1; }
}
for (const x of nTimes(3)) { console.log(x);
}
// 0
// 1
// 2
生成器作为默认迭代器
class Foo { constructor() { this.values = [1, 2, 3]; }* [Symbol.iterator]() { yield* this.values; }
}
const f = new Foo();
for (const x of f) { console.log(x);
}
// 1
// 2
// 3
for-of 循环调用了默认迭代器(它恰好又是一个生成器函数)并产生了一个生成器对象。
这个生成器对象是可迭代的,所以完全可以在迭代中使用。
提前终止生成器
与迭代器类似,生成器也支持“可关闭”的概念。一个实现 Iterator 接口的对象一定有 next()方法,还有一个可选的 return()方法用于提前终止迭代器。生成器对象除了有这两个方法,还有第三个方法:throw()。
function* generatorFn() {}
const g = generatorFn();
console.log(g); // generatorFn {<suspended>}
console.log(g.next); // f next() { [native code] }
console.log(g.return); // f return() { [native code] }
console.log(g.throw); // f throw() { [native code] }
小结
迭代是一种所有编程语言中都可以看到的模式。ECMAScript 6 正式支持迭代模式并引入了两个新的语言特性:迭代器和生成器。
迭代器必须通过连续调用 next()方法才能连续取得值,这个方法返回一个 IteratorObject。这个对象包含一个 done 属性和一个 value 属性。前者是一个布尔值,表示是否还有更多值可以访问;后者包含迭代器返回的当前值。这个接口可以通过手动反复调用 next()方法来消费,也可以通过原生消费者,比如 for-of 循环来自动消费。
生成器是一种特殊的函数,调用之后会返回一个生成器对象。生成器对象实现了 Iterable 接口,因此可用在任何消费可迭代对象的地方。生成器的独特之处在于支持 yield 关键字,这个关键字能够暂停执行生成器函数。使用 yield 关键字还可以通过 next()方法接收输入和产生输出。在加上星号之后,yield 关键字可以将跟在它后面的可迭代对象序列化为一连串值。
劝学 唐 孟郊
击石乃有火,不击元无烟。
人学始知道,不学非自然。
万事须己运,他得非我贤。
青春须早为,岂能长少年。
这篇关于《每天十分钟》-红宝书第4版-迭代器与生成器(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!