首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

详解 ECMAScript 6 中的生成器(2)生成器的基本用法

详解 ECMAScript 6 中的生成器(2)生成器的基本用法

生成器的基本用法在上一节中介绍了生成器的基本概念,下面介绍生成器的一些基本用法。
调用        next 方法时的参数首先来看一下代码清单 4 中的生成器函数 doMath。如果只是简单的看代码,可能会认为在调用生成器对象的 next 方法之后,x 的值应该是 1,y 的值应该是 11,z        的值应该是 110。
清单 4. 生成器函数        doMath
1
2
3
4
5
function *doMath() {
  let x = yield 1;
  let y = yield x + 10;
  let z = yield y * 10;
}




然而实际的执行结果并不符合我们的预期。如代码清单 5 所示,实际的值分别是 1,NaN 和 NaN。了解产生这样结果的关键点在于,在调用 next 方法时所传递的参数值,被作为上一个        yield 表达式的实际值。由于我们在调用 next 时没有传入任何参数,每个 yield 表达式的实际值都是 undefined。在第一个 next 调用中,由于没有上一个        yield 表达式,因此该值被忽略;在第二个 next 调用中,上一个 yield 表达式,也就是 yield 1 的值是 next 调用时的参数值,也就是 undefined,因此        x 的值是 undefined,所以 yield x + 10 的值是 NaN。由此类推,第三个 next 调用中,y 的值同样也是 undefined,因此产生的值也是        NaN。
清单 5. 使用生成器函数 doMath
1
2
3
4
5
6
7
8
9
let func = doMath();
func.next();
// -> {value: 1, done: false}
func.next();
// -> {value: NaN, done: false}
func.next();
// -> {value: NaN, done: false}
func.next();
// -> {value: undefined, done: true}




现在可以尝试在调用 next 方法时传递值。在代码清单 6 中,第二个 next 调用时传入了值 1,因此 1 被作为上一个 yield 表达式 yield 1 的值,把 x 设为        1,所以该 next 方法调用的值为 11;第三个 next 调用时传入了值 2,因此 2 被作为第二个 yield 表达式 yield x + 10 的值,把 y 设为 2,所以该        next 方法调用的值为 20。
清单 6. 在调用 next        方法时传递值
1
2
3
4
5
6
7
8
9
let func = doMath();
func.next();
// -> {value: 1, done: false}
func.next(1);
// -> {value: 11, done: false}
func.next(2);
// -> {value: 20, done: false}
func.next(3);
// -> {value: undefined, done: true}




使用        return 语句在生成器函数中,同样可以使用 return 语句。通过 return 返回的值也会被传递给 next 方法的调用者,同时会结束掉生成器对象的执行,也就是把属性 done 的值设为        true。在代码清单 7 中,第二个 next 的调用会执行到 return 语句并结束执行。
清单 7. 使用 return        语句
1
2
3
4
5
6
7
8
9
10
11
12
function *withReturn() {
  let x = yield 1;
  return x + 2;
}

let func = withReturn();
func.next();
// -> {value: 1, done: false}
func.next(1);
// -> {value: 3, done: true}
func.next();
// -> {value: undefined, done: true}




生成器与迭代器从之前使用生成器对象的代码中可以发现,next 方法的返回值并不是特别直观,需要通过属性 done        来判断是否还有值。实际上,这是因为生成器对象本身也是迭代器(iterator)对象,而迭代器对象用 next        方法来获取其中的值。同时生成器对象也是可被迭代的(iterable)。因此,我们可以用 ECMAScript 6 中的其他新特性来遍历其中的值,包括 for-of 循环,spread        操作符和新的集合类型。在代码清单 8 中,首先使用了 for-of 循环来遍历一个生成器对象,接着使用 spread 操作符把生成器对象的值添加到数组中,最后使用生成器中的值创建了一个        Set 对象。
清单 8. 以迭代器的方式来使用生成器
1
2
3
4
5
6
7
8
9
10
for (let value of sample()) {
  console.log(value);
}
// -> 输出 1,2 和 3
['a', ...sample(), 'b']
// -> [ 'a', 1, 2, 3, 'b' ]

let set = new Set(sample())
set.size
// -> 3




生成器函数的参数与普通函数一样,生成器函数也可以接受输入参数。这些参数可以在 yield 表达式中使用。在代码清单 9 中,生成器函数 seq 有两个参数 start 和        number,分别表示所产生值的初始值和值的数量。两个参数都有默认值。函数 debug 的作用是输出生成器中的全部值,在后面的代码中也会用到。
清单 9. 生成器函数的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function debug(values) {
  for (let value of values) {
    console.log(value);
  }
}

function *seq(start = 0, number = 10) {
  while (number-- > 0) {
    yield start++;
  }
}

debug(seq());
// -> 输出从 0 到 9 的值

debug(seq(3));
// -> 输出从 3 到 12 的值

debug(seq(3, 5));
// -> 输出从 3 到 7 的值

返回列表