迭代器
背景
1.什么是迭代器
- 从一个数据集合中按照一定的顺序,不断取出数据的过程
2.迭代和循环有什么区别?
- 迭代强调的是依次去数据,并不保证取多少,也不保证所有的数据都要取完
- 遍历:把整个数据依次的全部取出
3.迭代器
- 对迭代过程的封装,在不同语言中有不同的表现形式,通常是一个对象。
4.迭代模式
- 一种设计模式,用于统一迭代过程,并且规范了迭代器规格。
- –迭代器具有得到下一个数据的能力
- –迭代器具有判断是否还有后续数据的能力
JS中迭代器
JS规定
- 如果一个对象具有 next(),并且该方法能够返回一个对象
{value:值,done是否迭代完成}
则认为该对象是一个迭代器
含义:
- next()用于得到下一个数据
返回一个对象
value:下一个数据的值
done:boolean 是否迭代完成
不能理解没关系,看了理论再看看案例,通过案例来理解理论
创建迭代器案例
由下面代码结合理论可知:迭代器就是执行next()方法来获取一个result对象,result对象内部有属性value:本次运行获得的值,done:是否可继续迭代 (done的值由代码可知,当i的值大于或等于数组的的长度,说明已经遍历到最后一个数字了,那么done就为true)。然后i自增,等到下次运行的next方法返回的对象中value就为数组的第二个值,done会继续判断是否为false。下面中的while就是通过不断执行next方法通过返回的result对象中的value来遍历每一个数
javascript">//利用迭代器循环打印数组的所有数
const arr = [1,2,3,4,5,6,7,8,9];
const iterator ={
i:0, //当前数据的下标
next(){
let 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();//重复书写的目的是让i自增
}
//最终打印出:1,2,3,4,5,6,7,8,9
通过函数来创建迭代器 ↓
其实原理都一样,只不过需要先执行这个函数来获取到迭代器
javascript">let arr = [1,3,5,7,9];
// 创建一个迭代函数
function createiterator(arr){
let i = 0;
return{
next(){
let result = {
value:arr[i],
done:i>=arr.length
}
i++;
return result;
}
}
}
const itr1 = createiterator(arr);
console.log(itr1.next());
可迭代协议
ES6中规定, 如果对象具有知名符号 symbol.iterator
并且属性值是一个迭代器创建函数,则该对象是可以进行迭代的。
如图:可以看到,数组是具备这两个条件的,所以数组是可迭代对象:也就是说,我们可以不用创建迭代器,数组本身自带迭代器,通过属性调用就可以获得到它的迭代器了
除了数组也还有其他的可迭代对象。例如通过document.querySelectorAll()
获取的标签集合也是一个可迭代对象,因为具备符号 symbol.iterator
并且属性值是一个迭代器创建函数
可迭代对象可以通过隐式原型获取迭代器。
javascript">let arr = [1,2,3,4,5,6];
//获取迭代器
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while(!result.done){
console.log(result.value);
result = iterator.next();
}
//输出1,2,3,4,5,6
下图可以看到,纯对象就不是一个可迭代对象,它没有符号 symbol.iterator
for - of 循环
for - of用于遍历可迭代对象。
可以看到,简化了上面的代码。但条件是只能作用在可迭代对象上
for(const item of iterator){
item //每次迭代得到的数组
iterator //可迭代对象
}
javascript">let arr = [1, 2, 3, 4, 5, 6];
for(let item of arr){
console.log(item);
}
//输出1,2,3,4,5,6
根据上面的可迭代协议可以知道,纯对象不是一个可迭代对象,所以无法使用for-of,但是我们可以通过给它创建一个符号将它变成可迭代对象,然后调用for-of
javascript">let obj = {
name:"hc",
age:18,
[Symbol.iterator](){
const keys = Object.keys(this);
const Values = Object.values(this);
let i = 0;
return {
next(){
const Name = keys[i];
const Vlue = Values[i]
const result = {
value:{
Name,
Vlue,
},
done:i>=keys.length
}
i++;
return result
}
}
}
}
for(let iter of obj){
console.log(iter);
}
输出如下,但需要注意的是,我们创建的符号并没有被写入到隐式原型里面去(看红框框)
展开运算符和迭代对象
展开运算符可以作用于可迭代对象,这样我们就可以将可迭代对象转化为数组
javascript"> let obj = {
name: "hc",
age: 18,
[Symbol.iterator]() {
const keys = Object.keys(this);
const Values = Object.values(this);
let i = 0;
return {
next() {
const Name = keys[i];
const Vlue = Values[i]
// const Value = this.Name;
const result = {
value: {
Name,
Vlue,
},
done: i >= keys.length
}
i++;
return result
}
}
}
}
let arr = [...obj];
console.log(arr);
分析得出:在展开运算符解析对象的时候,此时的对象运行了一次next()方法。返回的值也是一个对象,然后赋值给arr。由于此对象中有两个键值对,故运行了两遍。
生成器
1.什么是生成器
- 更加方便的编写迭代器
- 生成器是一个通过
Generator
函数来创建的对象 - 生成器功能即是一个迭代器(因为可以通过生成器.next来进行迭代)
- 同时又是一个可迭代对象(生成器拥有Symbol.iterator)
2.如何去创建一个生成器
- 生成器的创建,必须使用生成器函数 Generator function
3.如何去创建一个生成器函数
<!-- 表示这是一个生成器函数 一定会返回一个生成器 -->
function* show(){
}
4.生成器函数内部是如何执行的
- 生成器函数内部是为了给生成器的每次迭代提供数据。
- 每次调用next方法 将导致生成器运行到 下一个 yield 关键字位置
- yield:是一个关键字 ,他只能在生成器内部使用 表示,产生一个迭代数据
创建一个生成器
根据上面的理论,可以简化理解成给function后面加个* 那么这个函数便成了一个生成器。通过将生成器赋值给一个变量,然后调用next方法来达到迭代器的作用。
运行流程
遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
javascript"> function* show(){
console.log("第一次执行");
yield 100000;
console.log("第二次执行");
yield 20;
}
let arr = show();
console.log(arr.next());
console.log(arr.next());
输出情况
也可以传参
javascript"> let arr = [1,3,5,7,9];
let arr2 = [2,4,6,8,10];
function* creatiterator(arr){
for(let item of arr){
yield item;
}
}
let item1 = creatiterator(arr);
let item2 = creatiterator(arr2);
console.log(item1.next());
console.log(item2.next());
生成器中的return
由下面例子可知。当在生成器中遇见return时会提前强行将done的值变成true,提前结束迭代,并且return后面的值为最后一次有效迭代的值。
javascript"> function* show(){
let num1 = "一袋米抗几楼"
console.log("第一次执行");
yield num1;
console.log("第二次执行");
yield 20;
console.log("第二次执行");
return "zyl真帅"
yield 30;
console.log("第二次执行");
yield 40;
console.log("第三次执行");
return "zyl真帅"
}
let arr = show();
console.log(arr.next());
执行结果
补充
javascript">function* t1(){
yield function show(){
// console.log(111);
return 111;
}
yield "b"
}
function* t2(){
yield* t1()
yield "c"
yield "d"
yield "e"
}
let test = t2();
console.log(test.next());
输出情况
可以看出yield后面跟着的值可以是多样化的,并且,上述代码中,show函数里面return并没有终止迭代。说明如果return不是直接拦截的话。迭代是不会终止的