ES6生成器总结
一、生成器的个人理解
首先什么是生成器:生成器就是Es6中用来设置迭代器的一个函数,我们就可以这样理解:生成器最终生成的就是迭代器。
生成器:生成器中存在一个iterator接口,也就是说,生成器本身是可以进行迭代的,也就是创造出一个生成器,就相当于创建一个迭代器。关于生成器的用途,对于一些没有设置迭代器接口的数据类型,我们可以使用生成器来自定义生成一个迭代器,从而进行迭代。
以上便是我对于生成器本身的理解,下面讲解具体的语法。
二、生成器的一些属性
//生成器的声明
<script>
function* generator(){
}
let test = generator();
console.log(test);
</script>
//生成器中的yield关键字
<script>
function* Generator(){
yield 100;
yield 200;
yield 300;
yield 400;
}
let test = Generator();
console.log(test.next());
console.log(test.next());
console.log(test.next());
console.log(test.next());
console.log(test.next());
console.log(test.next());
console.log(test.next());
</script>
最终结果:
解析:由于生成器实例化对象的原型中存在next(),当执行next(),就会根据yield进行迭代。
<script>
function* Generator(){
yield 100;
yield 200;
yield 300;
yield 400;
}
let test = Generator();
console.log(test[Symbol.iterator]);
</script>
又是上面的代码可以看出这个生成器存在Symbol.iterator接口,所以可以将其看成一个迭代器
生成器只会在使用初次调用函数的时候执行
<script>
function* Generator(){
console.log("初次执行函数");
yield 100;
yield 200;
yield 300;
yield 400;
}
let test = Generator();
console.log(test.next());
</script>
执行结果是:
在生成器中也会有接口,但是他们默认的迭代器是自引用的,所以下面两种迭代方式得到相同的结果
<script>
function* Generator(){
yield 100;
yield 200;
yield 300;
yield 400;
}
let test = Generator();
// for(const x of test){
// console.log(x);
// }
for(const x of test[Symbol.iterator]()){
console.log(x);
}
</script>
结果是:
生成器对象区分作用域
<script>
function* Generator() {
yield 100;
yield 200;
yield 300;
yield 400;
}
let test_1 = Generator();
let test_2 = Generator();
console.log(test_1.next());
console.log(test_2.next());
console.log(test_1.next());
console.log(test_2.next());
</script>
解析:生成器是按照游标进行输出的,也就是但是当作用域不同,两者的游标互相不影响。
结果:
实现yield关键字输出输入
<script>
function* Generator() {
console.log(yield 100);
console.log(yield 200);
console.log(yield 300);
console.log(yield 400);
}
let test = Generator();
console.log(test.next("foo"));
console.log(test.next("bar"));
console.log(test.next("btz"));
console.log(test.next('set'));
console.log(test.next("map"));
</script>
结果:
这里当我们传入第一个的时候,并没有输出,这是当我们执行第一个的next()的时候,相当于去开启执行这个函数,但是这个值并不传给yield,但是当执行第二个yield的时候,这个yield接收到传给的值并输出。
yield*的使用
<script>
// function* Generator() {
// yield* [1, 2, 3, 4, 5];
// }
// let test = Generator();
// for (const x of test) {
// console.log(x);
// }
function* Generator(){
let arr = [1,2,3,4,5];
for(const x in arr){
yield arr[x];
}
}
let test = Generator();
for (const x of test[Symbol.iterator]()){
console.log(x);
}
//1,2,3,4,5
</script>
以上两个方法进行迭代,都可以得到相同的结果,yiled*就相当于结构的思想。
yield* 的值最终返回的一定是undefined
<script>
function* Generator(){
console.log(yield* [1,2,3,4,5]);;
}
let test = Generator();
for (const x of test[Symbol.iterator]()){
console.log(x);
}
</script>
最终结果:
其实这里可以这样想yield * [1,2,3,4]就等价于 yield 1;yield 2,yield 3;yield 4;由于没有给yield传值,所以打印出来的是undefined。
yiled实现递归
<script>
function* Generator(n) {
if (n > 0) {
yield* Generator(n - 1);
yield n;
}
}
let text = Generator(10);
for (const x of text) {
console.log(x);
}
//1,2,3,4,5,6,7,8,9,10
</script>
yiled*后面必须接上一个具有iterator接口的数据类型
<script>
//第一种
function* gener_1(){
yield* [1,5,6,3,10];
}
function* gener_2(){
yield* gener_1();
}
for(const x of gener_2()){
console.log(x);
}
//1,5,6,3,10
</script>
这里我们实现手写三种迭代接口,这三种从上往下更加规范,简单
<script>
class Gener {
constructor() {
this.value = [1, 2, 3, 4, 5];
};
[Symbol.iterator]() {
let index = 0;
let value = this.value;
return {
next() {
if (index < value.length) {
return {
value: value[index++],
done: false,
}
}
else {
return {
value: undefined,
done: true
}
}
}
}
}
}
let test = new Gener();
for (const x of test) {
console.log(x);
}
//第二种
class Gener{
constructor() {
this.value = [1,2,3,4,5];
}
*[Symbol.iterator](){
for(const x of this.value){
yield x;
}
}
}
let test = new Gener();
for (const x of test){
console.log(x);
}
// 第三种
class Gener{
constructor(){
this.value = [1,2,3,4,5];
}
*[Symbol.iterator](){
yield* this.value;
}
}
let test = new Gener();
for(const x of test){
console.log(x);
}
</script>
这三种最终的结果都是一样的,为什么要设置[Symbol.iteerator],是因为class对象中不可以进行迭代。
return方法
<script>
function * text(){
yield* [1,2,3,4,5];
}
let g = text();
console.log(g.return("3"));
</script>
在这里执行return()的时候,就会返回其中传入return的值,并且将其done设置为true
。
关于生成器中的throw()
<script>
function* text() {
for (const x of [1, 2, 3]) {
try {
yield x;
}
catch {
console.log("这是一个错误");
}
}
}
let test = text();
console.log(test.next());
console.log(test.throw("hhhh"));
console.log(test.next());
console.log(test.next());
</script>
解析:这里一般执行throw()的时候,都会伴随着try…catch一起使用。当执行这个throw()函数的时候,就相当于传一个错误,所以就不会执行try中的语句,而是执行catch中的语句。然后不会像return()一样直接退出迭代,throw()会跳过这个进入下一个。
以上便是我对于ES6的生成器的总结,以及自己的理解。总结出来时间已经比较晚了,加油,明天进入面向对象。