红宝书——07.迭代器与生成器

目录

1.迭代器

1.可迭代协议

2.迭代器协议

3.自定义迭代器

2.生成器

基本实现

通过 yield 中断执行

提前终止的方法


1.迭代器

1.可迭代协议

实现 Iterable 接口(可迭代协议)要求同时具备两种能力:支持迭代的自我识别能力和创建实现
Iterator 接口的对象的能力。在 ECMAScript 中,这意味着必须暴露一个属性作为“默认迭代器”,而
且这个属性必须使用特殊的 Symbol.iterator 作为键。

很多内置类型都实现了 Iterable 接口:
 字符串
 数组        
 映射
 集合
 arguments 对象
 NodeList 等 DOM 集合类型

// 这两种类型没有实现迭代器工厂函数
console.log(num[Symbol.iterator]); // undefined
console.log(obj[Symbol.iterator]); // undefined
let str = 'abc';

// 这些类型都实现了迭代器工厂函数
console.log(str[Symbol.iterator]); // f values() { [native code] }

// 调用这个工厂函数会生成一个迭代器
console.log(str[Symbol.iterator]()); // StringIterator {}

接收可迭代对象的原生语言特性包括:
 for-of 循环
 数组解构
 扩展操作符
 Array.from()
 创建集合
 创建映射
 Promise.all() 接收由期约组成的可迭代对象
 Promise.race() 接收由期约组成的可迭代对象
 yield* 操作符,在生成器中使用
这些原生语言结构会在后台调用提供的可迭代对象的这个工厂函数,从而创建一个迭代器

2.迭代器协议

迭代器是一种一次性使用的对象,用于迭代与其关联的可迭代对象。迭代器 API 使用 next() 方法
在可迭代对象中遍历数据。每次成功调用 next() ,都会返回一个 IteratorResult 对象,其中包含迭代器返回的下一个值。若不调用 next() ,则无法知道迭代器的当前位置。
next() 方法返回的迭代器对象 IteratorResult 包含两个属性: done 和 value 。 done 是一个布
尔值,表示是否还可以再次调用 next() 取得下一个值; value 包含可迭代对象的下一个值( 如果value存在则done 为false ),或者 undefined ( done 为 true )。 done: true 状态称为“耗尽”。

3.自定义迭代器

为了让一个可迭代对象能够创建多个迭代器,必须每创建一个迭代器就对应一个新计数器。为此,
可以把计数器变量放到闭包里,然后通过闭包返回迭代器:
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, value: undefined };
                                }
                        }
                };
        }
}
let counter = new Counter(3);
for (let i of counter) { console.log(i); }
// 1
// 2
// 3

2.生成器

生成器是 ECMAScript 6 新增的一个极为灵活的结构,拥有在一个函数块内暂停和恢复代码执行的
能力。这种新能力具有深远的影响,比如,使用生成器可以自定义迭代器和实现协程。

基本实现

生成器的形式是一个函数,函数名称前面加一个星号( * )表示它是一个生成器。只要是可以定义
函数的地方,就可以定义生成器。箭头函数不能用来定义生成器函数。

生成器对象也实现了 Iterator 接口,因此具有 next() 方法。调用这个方法会让生成器
开始或恢复执行。

通过 yield 中断执行

yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 yield
关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数只能通过在生成器对象上调用 next() 方法来恢复执行。

yield 关键字只能在生成器函数内部使用,用在其他地方会抛出错误。类似函数的 return 关键字,
yield 关键字必须直接位于生成器函数定义中,出现在嵌套的非生成器函数中会抛出语法错误。

第一次调用 next() 传入的值不会被使用,因为这一次调用是为了开始执行生成器函数

function* generatorFn(initial) {
        console.log(initial);
        console.log(yield);
        console.log(yield);
}
let generatorObject = generatorFn('foo');
generatorObject.next('bar'); // foo
generatorObject.next('baz'); // baz
generatorObject.next('qux'); // qux

提前终止的方法

与迭代器类似,生成器也支持“可关闭”的概念。一个实现 Iterator 接口的对象一定有 next()
方法,还有一个可选的 return() 方法用于提前终止迭代器。生成器对象除了有这两个方法,还有第三个方法: throw() 。

1. return()

return() 方法会强制生成器进入关闭状态。提供给 return() 方法的值,就是终止迭代器对象的值:
function* generatorFn() {
        for (const x of [1, 2, 3]) {
        yield x;
        }
}
const g = generatorFn();
console.log(g); // generatorFn {<suspended>}
console.log(g.return(4)); // { done: true, value: 4 }
console.log(g); // generatorFn {<closed>}

后续调用 next() 会显示 done: true 状态,而提供的任何返回值都不会被存储或传播

2. throw()

throw() 方法会在暂停的时候将一个提供的错误注入到生成器对象中。如果错误未被处理,生成器
就会关闭

function* generatorFn() {
        for (const x of [1, 2, 3]) {
                yield x;
        }
}
const g = generatorFn();
console.log(g); // generatorFn {<suspended>}
try {
        g.throw('foo');
} catch (e) {
        console.log(e); // foo
}
console.log(g); // generatorFn {<closed>}

不过,假如生成器函数内部处理了这个错误,那么生成器就不会关闭,而且还可以恢复执行。错误
处理会跳过对应的 yield ,因此在这个例子中会跳过一个值。

function* generatorFn() {
        for (const x of [1, 2, 3]) {
                try {        
                        yield x;
                } catch(e) {}
        }
}
const g = generatorFn();
console.log(g.next()); // { done: false, value: 1}
g.throw('foo');
console.log(g.next()); // { done: false, value: 3}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值