理解迭代器的概念
什么是迭代?
从一个数据集合中按照一定的顺序,不断取出数据的过程
迭代和遍历的区别?
迭代强调的是依次取数据,并不保证取多少,也不保证把所有的数据取完
遍历强调的是要把整个数据依次全部取出
例:在生活中,我们经常听到一个词叫产品迭代,一个产品有可能做到一半,公司倒闭导致产品无法继续迭代
什么是迭代器?
对迭代过程的封装,在不同的语言中有不同的表现形式,通常为对象
迭代模式
一种设计模式,用于统一迭代过程,并规范了迭代器规格:
- 迭代器应该具有得到下一个数据的能力
- 迭代器应该具有判断是否还有后续数据的能力
JS中的迭代器
JS规定,如果一个对象具有next方法,并且该方法返回一个对象,该对象的格式如下:
{
value: 值,
done: 是否迭代完成
}
则认为该对象是一个迭代器
数组迭代器
const arr = [1, 2, 3, 4, 5];
//迭代数组arr
const iterator = {
i: 0, //当前的数组下标
next() {
var result = {
value: arr[this.i],
done: this.i >= arr.length
}
this.i++;
return result;
}
}
//让迭代器不断的取出下一个数据,直到没有数据为止
let data = iterator.next();
while (!data.done) { //只要没有迭代完成,则取出数据
console.log(data.value)
//进行下一次迭代
data = iterator.next();
}
console.log("迭代完成")
这个iterator对象有next方法,并且返回一个包含value和down的对象,我们认为它是一个迭代器
什么时候适合使用迭代器?
当数据量无穷或者数据量很大的时候,适合使用迭代器
比如依次取菲波那切数列的值
function createFeiboIterator() {
let prev1 = 1,
prev2 = 1, //当前位置的前1位和前2位
n = 1; //当前是第几位
return {
next() {
let value;
if (n <= 2) {
value = 1;
} else {
value = prev1 + prev2;
}
const result = {
value,
done: false
};
prev2 = prev1;
prev1 = result.value;
n++;
return result;
}
}
}
const iterator = createFeiboIterator();
斐波那契数列无穷无尽,而且我们每次取得时候都是只关心下一个,所以适合迭代器
迭代器创建函数
当我们需要给两个数组分别创建迭代器的时候,那么会有冗余代码,这时候我们可以声明一个函数,函数返回一个迭代器,
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [6, 7, 8, 9];
// 迭代器创建函数 iterator creator
function createIterator(arr) {
let i = 0;//当前的数组下标
return {
next() {
var result = {
value: arr[i],
done: i >= arr.length
}
i++;
return result;
}
}
}
const iter1 = createIterator(arr1);
const iter2 = createIterator(arr2);
可迭代协议 与 for-of 循环
可迭代协议
ES6规定,如果一个对象具有知名符号属性Symbol.iterator
,并且属性值是一个迭代器创建函数,则该对象是可迭代的(iterable)
//可迭代对象
var obj = {
a: 1,
b: 2,
[Symbol.iterator]() {
const keys = Object.keys(this);
let i = 0;
return {
next: () => {
const propName = keys[i];
const propValue = this[propName];
const result = {
value: {
propName,
propValue
},
done: i >= keys.length
}
i++;
return result;
}
}
}
}
for (const item of obj) {
console.log(item); // {propName:"a", propValue:1}
}
for-of 循环
for-of 循环用于遍历可迭代对象,格式如下
//迭代完成后循环结束
for(const item in iterable){
//iterable:可迭代对象
//item:每次迭代得到的数据
}
展开运算符与可迭代对象
展开运算符可以作用于可迭代对象,这样,就可以轻松的将可迭代对象转换为数组。
生成器
什么是生成器?
生成器是一个通过构造函数Generator创建的对象,生成器既是一个迭代器,同时又是一个可迭代对象
如何创建生成器?
生成器的创建,必须使用生成器函数(Generator Function)
如何书写一个生成器函数呢?
//这是一个生成器函数,该函数一定返回一个生成器
function* method(){
}
生成器函数内部是如何执行的?
生成器函数内部是为了给生成器的每次迭代提供的数据
每次调用生成器的next方法,将导致生成器函数运行到下一个yield关键字位置
yield是一个关键字,该关键字只能在生成器函数内部使用,表达“产生”一个迭代数据。
例:斐波那契数列生成器
function* feibo() {
let last1 = 1,
last2 = 1,
i = 0;
while (1) {
if (i <= 1) {
yield 1
} else {
let result = last1 + last2;
yield result;
last1 = last2;
last2 = result
}
i ++
}
}
let generator = feibo();
console.log(generator.next())
next的参数和return
生成器的可迭代对象的next方法可接受一个参数,这个参数会赋值给yield表达式
function *test() {
let v1 = yield 1;
console.log(v1);
let v2 = yield 2 + v1;
console.log(v2);
return 99
}
let generator = test();
console.log(generator.next())
有哪些需要注意的细节?
1). 生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中
2). 调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值
3). 第一次调用next方法时,传参没有任何意义
4). 在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号
生成器的其他API
- return方法:调用该方法,可以提前结束生成器函数,从而提前让整个迭代过程结束
- throw方法:调用该方法,可以在生成器中产生一个错误,并提前结束迭代过程
function* feibo() {
let last1 = 1,
last2 = 1,
i = 0;
while (1) {
if (i <= 1) {
yield 1
} else {
let result = last1 + last2;
yield result;
last1 = last2;
last2 = result
}
i ++
}
}
let generator = feibo();
console.log(generator.next())
console.log(generator.return(100)) // {value: 100, down: true}
function* feibo() {
let last1 = 1,
last2 = 1,
i = 0;
while (1) {
if (i <= 1) {
yield 1
} else {
let result = last1 + last2;
yield result;
last1 = last2;
last2 = result
}
i ++
}
}
let generator = feibo();
console.log(generator.next())
console.log(generator.throw(100)) // {value: 100, down: true}
console.log(generator.next()) // {value: undefined, done: true}
生成器的应用
生成器在react源码中有大量的使用,生成器弥补了es6还没有async和await的缺陷
可以使用使用生成器完成优雅的异步调用
function* task() {
const resp = yield fetch("https://developer.duyiedu.com/edu/groupChat/getMsgList")
const result = yield resp.json();
console.log(result);
}
run(task)
function run(generatorFunc) {
const generator = generatorFunc();
let result = generator.next(); //启动任务(开始迭代), 得到迭代数据
handleResult();
//对result进行处理
function handleResult() {
if (result.done) {
return; //迭代完成,不处理
}
//迭代没有完成,分为两种情况
//1. 迭代的数据是一个Promise
//2. 迭代的数据是其他数据
if (result.value instanceof Promise) {
//1. 迭代的数据是一个Promise
//等待Promise完成后,再进行下一次迭代
result.value.then(data => {
result = generator.next(data)
handleResult();
})
} else {
//2. 迭代的数据是其他数据,直接进行下一次迭代
result = generator.next(result.value)
handleResult();
}
}
}