目录
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}