1、什么是Iterator
1.1、Iterator作用
Iterator 的作用有三个:
- 为各种数据结构,提供一个统一的、简便的访问接口
- ES6 创造了一种新的遍历命令
for..of
循环,Iterator 接口主要供for..of
消费
1.2、什么数据结构能被称为iterator
一个数据结构只要部署了
Symbol.iterator
属性,就被视为具有 iterator 接口;
Symbol.iterator规则
- 内部必须实现
next()
方法 - 迭代器的
next()
方法: -
- 必须返回一个具有
done
和value
属性的对象。
- 必须返回一个具有
-
done
(布尔值):表示迭代是否结束。
-
value
:当前迭代的值(done
为true
时可省略)。
1.3、内置iterator的数据结构
- 字符串
- 数组
Map
Set
arguments
对象NodeList
等 DOM 集合类型
1.4、iterator的数据结构特性
for-of
循环- 数组解构
- 扩展操作符
Array.from()
参数传入一个类数组或者迭代器- 创建
Set
- 创建
Map
Promise.all()
接收由Promise
组成的可迭代对象Promise.race()
接收由Promise
组成的可迭代对象yield*
操作符,在生成器中使用
1.5、手写Symbol.iterator实现解构与扩展操作
手写symbol.iterator
js
let obj = {
name: "tom",
age: 18,
gender: "男",
intro: function () {
console.log("my name is " + this.name);
},
[Symbol.iterator]: function () {
let i = 0;
// 获取当前对象的所有属性并形成一个数组
let keys = Object.keys(this);
return {
next: function () {
return {
// 外部每次执行next都能得到数组中的第i个元素
value: keys[i++],
// 如果数组的数据已经遍历完则返回true
done: i > keys.length,
};
},
};
},
};
let [a, b] = obj;
console.log(a); //name
console.log(b); //age
console.log([...obj]);//[ 'name', 'age', 'gender', 'intro' ]
盗用symbol.iterator
通过
Object.keys
或者Object.values
将对象转换为数组,盗用数组的iterator
js
let obj = {
name: "tom",
age: 18,
gender: "男",
intro: function () {
console.log("my name is " + this.name);
},
};
obj[Symbol.iterator] = function () {
let arr = Object.keys(this);
return arr[Symbol.iterator]();
};
let [a, b] = obj;
console.log(a); //name
console.log(b); //age
console.log([...obj]); //[ 'name', 'age', 'gender', 'intro' ]
2、Generator
普通对象的
Iterator
,开发人员还需手动调用next()
,获取value
并判断done
遍历是否中断
于是Generator
函数就应运而生了,它被设计出来的目的就是:简化Iterator函数
的编写。
js
let obj = {
name: "tom",
age: 18,
gender: "男",
intro: function () {
console.log("my name is " + this.name);
},
// 使用 Generator 函数实现迭代器
[Symbol.iterator]: function* () {
// 获取当前对象的所有属性并形成一个数组
let keys = Object.keys(this);
// 使用 for...of 循环遍历 keys 数组并逐个 yield 每个属性名
for (let key of keys) {
yield key;
}
},
};
3.1、利用generator实现链式网络请求
js
function request(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url);
}, 2000);
});
}
function* getData() {
const res1 = yield requestData("why")
console.log("res1:", res1)
const res2 = yield requestData(res1 + "kobe")
console.log("res2:", res2)
const res3 = yield requestData(res2 + "james")
console.log("res3:", res3)
}
const generator = getData()
generator.next().value.then(res1 => {
generator.next(res1).value.then(res2 => {
generator.next(res2).value.then(res3 => {
generator.next(res3)
})
})
})
3.Iterator+Generator 实现async await
Generator 生成器函数的特点就是卡顿
每次运行到 yield 语句时就会"卡住",需要调用其返回的 Iterator 对象的 next 方法之后,才会继续往下执行,然后又在下一次 yield 处"卡住"。
以此我们可以自己手动实现一个递归函数:
- 接受一个
生成器函数
- 内部实现一个递归函数
- 递归函数内部执行next()方法
- 判断 done 属性:
- done 为 true:结束
- done 为 false:
- value是一个Promise
- 调用 promise.then 方法,等待异步操作完成。取出其兑现值,进行递归,并将兑现值作为参数传入
js
function request(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url);
}, 2000);
});
}
function* getData() {
const res1 = yield request("1");
console.log("res1:", res1);
const res2 = yield request(res1 + "2");
console.log("res2:", res2);
const res3 = yield request(res2 + "3");
console.log("res3:", res3);
}
function execGenFn(fn) {
// 1.获取对应函数的generator
const gen = fn();
// 2.定义一个递归函数
function exec(res) {
// result -> { done: true/false, value: 值/undefined }
const result = gen.next(res);
if (result.done) return;
result.value.then((res) => {
exec(res);
});
}
// 3.执行递归函数
exec();
}
execGenFn(getData);