《TypeScript实战指南》—2.1.7 iterator和generator

举报
华章计算机 发表于 2019/06/16 11:29:05 2019/06/16
【摘要】 本节书摘来自华章计算机《TypeScript实战指南》一书中的第2章,第2.1.7节,作者是胡桓铭。

2.1.7 iterator和generator

1. iterator

当一个对象实现了Symbol.iterator时,我们认为它是可迭代的。如array、map、set、string、int32Array、uint32Array等一些内置的类型,目前都已经实现了各自的Symbol.iterator。对象上的Symbol.iterator函数负责返回供迭代的值。

for..of语句会遍历可迭代的对象,调用对象上的Symbol.iterator方法。

比如下面是在数组上使用for..of的例子:

const array = [233, "hello", true];

 

for (let value of array) {

    console.log(value); // 233, "hello", true

}

for..of和for..in都可以迭代一个数组,但它们之间的区别很大。最明显的区别莫过于它们用于迭代器的返回值并不相同,for..in迭代的是对象的键,而for..of迭代的是对象的值。

我们可以从下面的例子中看出两者之间的区别:

const array = [3, 4, 5];

 

for (let i in array) {

    console.log(i); // 0, 1, 2

}

 

for (let i of array) {

    console.log(i); // 4, 5, 6

}

另一个区别在于,for..in可以操作任何对象,提供了查看对象属性的一种方法。但是for..of关注迭代对象的值,内置对象Map和Set已经实现了Symbol.iterator方法,让我们可以访问它们的值:

const fruits = new Set(["apple", "pear", "mango"]);

fruits["peach"] = "Princess Peach! Make a wish!";

 

for (let fruit in fruits) {

    console.log(fruit); // "peach"

}

 

for (let fruit of fruits) {

    console.log(fruit); // "apple", "pear", "peach"

}

但这样的特性仅仅在 ES 6 及以上才上生效。

当我们将 TypeScript 的代码生成目标设定为ES5或ES3,迭代器就只允许在array类型上使用。在非数组值上使用for..of语句会得到一个错误。即便这些非数组值已经实现了Symbol.iterator属性,也是不可以的。

编译器会生成一个简单的for循环作为for..of循环,比如:

const numbers = [1, 2, 3];

for (let number of numbers) {

    console.log(number);

}

生成的代码为:

var numbers = [1, 2, 3];

for (var _i = 0; _i < numbers.length; _i++) {

    var number = numbers[_i];

    console.log(number);

}

2. generator

function * 是用来创建  generator 函数的语法。(在 MDN 的文档中 generator 称为生成器。)

调用 generator 函数时会返回一个generator 对象。generator 对象遵循迭代器接口,即通常所见到的 next、return 和 throw 函数。

generator函数用于创建懒迭代器,例如下面的这个函数可以返回一个无限整数的列表:

function* infiniteList() {

    let i = 0;

    while(true) {

        yield i++;

    }

}

 

var iterator = infiniteList();

while (true) {

    console.log(iterator.next()); // { value: xxxx, done: false }

}

当然,也可以设定某个条件终止它,而不只是永远循环下去。如下所示:

function* infiniteList(){

    let i = 0;

    while(i < 3)

        yield i++;

}

 

let gen = infiniteList();

console.log(gen.next()); // { value: 0, done: false }

console.log(gen.next()); // { value: 1, done: false }

console.log(gen.next()); // { value: 2, done: false }

console.log(gen.next()); // { value: undefined, done: true }

可以说这个设定是generator中最令人兴奋的部分。它在实质上允许一个函数可以暂停执行,比如当我们执行了第一次的 gen.next() 后,可以先去做别的事,再回来继续执行 gen.next(),这样剩余函数的控制权就交给了调用者。

当你直接调用 generator 函数时,它并不会执行,它只会创建一个 generator 对象。

在下面的例子中,我们可以看到一个更灵活的使用方式:

function* generator(){

    console.log('Execution started');

    yield 0;

    console.log('Execution resumed');

    yield 1;

    console.log('Execution end');

}

执行它,则会看到如下输出结果:

constiterator = generator();

 

console.log(iterator.next());

// "Execution started"

// { value: 0, done: false }

 

console.log(iterator.next()); // { value: 1, done: false }

// "Execution resumed'"

// { value: 1, done: false }

 

console.log(iterator.next());

// "Execution end"

// { value: undefined, done: true }

 

console.log(iterator.next());

// { value: undefined, done: true }

从上面代码可以得知:

  • generator 对象只会在调用next时开始执行。

  • 函数在执行到yield语句时会暂停并返回yield的值。

  • 函数在next被调用时继续恢复执行。

所以实质上generator函数的执行与否是由外部的 generator 对象控制的。

不过除了 yield 传值到外部,我们也可以通过 next 传值到内部进行调用。下面的例子展示了iterator.next传值的方式:

function* generator() {

    const who = yield;

    console.log('hello '+ who); // bar!

}

 

const iterator = generator();

 

Console.log(iterator.next());

// {value: undefined, done: false}

 

console.log(iterator.next('TypeScript'));

// hello TypeScript

// {value: undefined, done: true}

以上便是 next 和 return 函数的内容,接下来我们来看一下 throw 函数如何处理迭代器内部报错。

下面是iterator.throw的例子:

function* generator() {

    try {

        yield 1;

    }

    catch(error) {

        console.log(error.message);

    }

}

 

const iterator = generator();

iterator.next()

// {value: 1, done: false}

iterator.throw(new Error('something incorrect'));

// something incorrect

// {value: undefined, done: true}

通过以上的案例我们可以得知,外部是可以对 generator 内部进行干涉的:

  • 外部系统可以传递一个值到 generator 函数体中。

  • 外部系统可以抛入一个异常到 generator 函数体中。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。