Chapter04---迭代器和生成器
一、迭代器
1. 可迭代协议:
-
JavaScript中支持迭代的内置类型:
字符串、数组、Map、Set、arguments对象、NodeList
等对象。 -
接收可迭代对象的原生语言特性包括:
for-of
、数组解构
、扩展操作符(...
)、Array.from()
、创建Map
和Set
、Promise.all()
、Promise.race()
、yield*
操作符。// for-of const str = "string"; for (const char of str){ console.log(char); } // 数组解构 const arr = ['foo', 'bar', 'baz']; const [a, b, c] = arr; console.log(a, b, c); // foo bar baz // 拓展操作符(...) const newArr = [...arr]; console.log(newArr); // ['foo', 'bar', 'baz'] // Array.from() const arr2 = Array.from([1, 2, 3, 4]); console.log(arr2); // [1, 2, 3, 4] //创建Map const set = new Set(arr); console.log(set); //创建Set const map = arr.map((x, i) => [i, x]); console.log(map);
2. 迭代器协议:
1.迭代器是一种一次性使用的对象,用于迭代与其关联的可迭代对象,使用next()
在可迭代对象中遍历数据 。
2.迭代器维护着一个指向可迭代对象的引用,因此迭代器会阻止垃圾回收程序回收可迭代对象 。
3.迭代器并不与可迭代对象某个时刻的快照绑定,而仅仅是使用游标来记录遍历可迭代对象的历程。
自定义迭代器:
主要是实现next()
、[Symbol.iterator]()
方法, 实现return()
方法用于迭代提前关闭时执行的逻辑。
自定义一次性计数迭代器:
//自定义迭代器
class Counter {
constructor(limit) {
this.count = 1;
this.limit = limit;
}
next() {
if (this.count <= this.limit) {
return {
value: this.count++,
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
[Symbol.iterator]() {
return this;
}
}
const counter = new Counter(10);
for (const i of counter) {
console.log(i);// 打印 1 2 3 4 5 6 7 8 9 10
}
for (const i of counter) { //不可二次迭代
console.log(i);// 不会打印
}
自定义重复性计数迭代器:
class NewCounter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() { //利用闭包实现多次迭代
let count = 1,
limit = this.limit;
return {
next() {
if (count <= limit) {
return {
value: count++,
done: false
};
} else {
return {
value: undefined,
done: true
};
}
},
return () {
console.log("iterator is exiting early");
return {
done: true,
value: undefined
};
}
}
}
}
const newCounter = new NewCounter(10);
for (const el of newCounter) {
console.log(el);
}
// 再次迭代
for (const el of newCounter) {
if (el > 5){ // 提前结束迭代器
break;
}
console.log(el);
}
二、生成器
1.概述:
1.生成器的形式就是一个函数,函数名称前加一个(*)表示它是一个生成器
。
2.生成器函数一开始处于暂停执行的状态,只会在初次调用next()方法后开始执行
。
3.生成器对象实现了Iterator接口,它们默认的迭代器是自引用
。
const myGenerator = function* (){
console.log("打印日志");
}
const genertorObj = myGenerator();
console.log(genertorObj);
genertorObj.next();//打印日志
2.yield关键字:
1.yield关键字
可以让生成器暂停和执行。
2.生成器函数在遇到yield关键字
之前会正常执行代码,一旦遇到yield
,就会停止执行,函数作用域 的状态会被保留,想要再次执行,可以通过调用next()来恢复。
3.一般来说,显示调用next()
并不太方便,同时生成器对象可以当做可迭代对象,因此在开发中常常 将生成器对象当做可迭代对象来使用。
4.可以利用yield
实现输入输出。
5.使用*
增强yield
的行为,让它能够迭代一个可迭代对象,从而一次产生一个值。
6.yield
可以优雅地实现递归式。
function* generatorFn(){
yield "boo";
yield "bar";
return "over";
}
const gObj = generatorFn();
console.log(gObj.next()); // { value: "boo", done: false }
console.log(gObj.next()); // { value: "bar", done: false }
console.log(gObj.next()); // { value: "over", done: true }
const obj = generatorFn();
for(const val of obj){
console.log(val);
}
// 产生n个0
function* zeroes(n){
while(n--){
yield 0;
}
}
//产生a到b之间的整数
function* range(a, b){
while(b >= a){
yield a++;
}
}
console.log(Array.from(zeroes(10))); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
console.log(Array.from(range(1, 10))); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
function* fn(){
yield* [1, 2, 3, 4];
}
for(const i of fn()){
console.log(i); // 1 2 3 4
}
// 递归
function* nTimes(n){
if(n > 0){
yield* nTimes(n-1);
yield n;
}
}
for(const i of nTimes(10)){
console.log(i); // 1 2 3 4 5 6 7 8 9 10
}
3.提前终止生成器:
实现return()
方法用于迭代提前关闭时执行的逻辑,throw()
也会使生成器提前关闭。
let nObj = nTimes(10);
try{//尝试提前关闭生成器对象
nObj.throw("foo");
}catch(e){
console.log(e);
}
console.log(nObj);
for(const i of nObj){//不会打印
console.log(i);
}