迭代
迭代有着一套属于自己的迭代协议,这规定了迭代与实现的逻辑,它之所以能够工作是因为依靠着迭代器(具体的迭代实现逻辑)、迭代对象(实现了[Symbol.iterator]方法的可被迭代对象)和迭代语句(例如for...in和for..of)
两种迭代语句的效果
for...in针对数组的演示案例一:
let arr=['a','b','c','d']
for(var i in arr){
console.log(i, arr[i])
}
//执行效果为
0 a
1 b
2 c
3 d
for...of针对数组的演示案例二:
let arr=['a','b','c','d']
for(var i of arr){
console.log(i)
}
//执行效果为:
a
b
c
d
- 由上面两个针对数组案例中可以看出两种语句的差别,我们打印出数组来查看一下数组的特别之处(
console.log(arr)),我们可以发现数组都有着Symbol.iterator属性 for...in针对对象的演示案例一:
var obj={
a:1,
b:2
}
for(var i in obj){
console.log(i,obj[i])
}
//执行的顺序为
a 1
b 2
for...of针对对象的演示案例二:
var obj={
a:1,
b:2
}
for(var i of obj){ //obj is not iterable
console.log(i,obj[i])
}
//执行的顺序为
Uncaught TypeError: obj is not iterable
- 由上面两个针对对象案例中可以看出两种语句的差别,我们打印出对象
(console.log(obj))来查看一下对象是否有实现[Symbol.iterator],结果是对象上默认是不支持迭代的,没有[Symbol.iterator],这也就解释了为什么通过for...of默认迭代不了对象的原因。
给对象搞上自定义的迭代协议
我们知道for...of默认是不能对对象进行迭代的,因为对象上是没有[Symbol.iterator]属性的,如果我们想让对象也能被for...of语句迭代,那我们可以给对象搞上自定义的迭代协议
- 第一步:创建一个对象并给其添加上属性
[Symbol.iterator]
let obj = {
a: 1,
b: 2,
c: 3
};
obj[Symbol.iterator] = function(){
}
//调用for of 时会去调用obj[Symbol.iterator]
for(let val of obj){
console.log(val);
}
- 第一步的执行结果为:
Uncaught TypeError: Result of the Symbol.iterator method is not an object这是因为我们没有在obj[Symbol.iterator]中去返回一个对象(return {})
- 第二步:给
obj[Symbol.iterator]中去返回一个对象(return {})
let obj = {
a: 1,
b: 2,
c: 3
};
obj[Symbol.iterator] = function(){
return {}
}
//调用for of 时会去调用obj[Symbol.iterator]
for(let val of obj){
console.log(val);
}
- 第二步的执行结果为:
Uncaught TypeError: undefined is not a function这是因为我们没有在return{}里定义next()方法
- 第三步:在
return{}里定义next()方法
let obj = {
a: 1,
b: 2,
c: 3
};
obj[Symbol.iterator] = function(){
return {
next(){}
}
}
//调用for of 时会去调用obj[Symbol.iterator]
for(let val of obj){
console.log(val);
}
- 第三步的执行结果为:
Uncaught TypeError: Iterator result undefined is not an object这是因为在next()[}里还需要返回一个返回值(对象),该返回值上面要有两个属性,一个是done,一个是value
第四步:使用迭代规则let values = Object.values(obj);的最终的改造结果
let obj = {
a: 1,
b: 2,
c: 3
};
obj[Symbol.iterator] = function(){
// 迭代协议--希望根据什么规则来循环
// values=[1,2,3]
let values = Object.values(obj);
// console.log(values) //[1,2,3]
// 用来遍历values的值 values[0]、 values[1]、 values[2]
let index = 0;
return {
next(){
if(index >= values.length){
return {
//是否循环是否遍历迭代完成
//循环结束时不会在走到for-of里面
//循环执行完成就算这里写了value也是没有用的不会再影响循环结果了
done: true,
value:'无用'
}
} else {
return {
//一直没有执行完成
done: false,
//value是我们在循环过程中的值
value: values[index++]
}
}
}
}
};
- 第四步的调用结果:
//调用for of 时会去调用obj[Symbol.iterator]
//然后每一次会去执行next()方法,
//next()方法中的判断条件决定了循环的结果
//并把value值返回到这里的val里
for(let val of obj){
console.log(val);
}
//执行顺序为
1
2
3
- 第五步:我们可以换一种方式来验证第四步的执行步骤
let values = obj[Symbol.iterator]();
console.log(values.next());
// {done:false,value:1}
console.log(values.next());
// {done:false,value:2}
console.log(values.next());
// {done:false,value:3}
//一旦执行到done:true,
//就算这里写了value也是没有用的不会再影响循环结果了
console.log(values.next());
// {done:true, value: "无用"}
第六步:我们可以发现第四步使用的迭代规则是let values = Object.values(obj);我们同样可以换一种迭代规则来迭代,接下来我们使用let keys = Object.keys(obj);
let obj = {
a: 1,
b: 2,
c: 3
};
obj[Symbol.iterator] = function(){
let keys = Object.keys(obj);
//[a,b,c]
let index = 0;
return {
next(){
if(index >= keys.length){
return {
done: true
}
} else {
return {
done: false,
value: {
key: keys[index],
value: obj[keys[index++]]
}
}
}
}
}
};
for(let val of obj){
console.log(val);
}
不经常用的Generator
- 没有async/await之前依靠的Generator+promise
- generator是基于迭代器实现的迭代函数
- 案例一:
function*fn(){
//yield定义的是每次循环返还的值
//yield定义的其实是next()方法
//这里的1是next方法中返回的value值
yield 1;
yield 2;
yield 3;
}
let f = fn();
// console.log(f); //不会立即执行 返回一个函数
//console.log(f.next()); //想要执行得调用next()方法 //第一次返回{value:1,done:false}
for(let val of f){
console.log(val);
}
- 案例二:
function*fn(){
yield new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("a");
resolve('第1个Promise需要传递的数据');
},500);
});
yield new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("b");
resolve('第2个Promise需要传递的数据');
},500);
});
yield new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("c");
resolve('第3个Promise需要传递的数据');
},500);
});
}
fn(); //一个都没有显示 //不会立即执行 返回一个函数
- 我们通过for-of来直接调用fn返回的函数,但是我们发现三个promise也是立即执行不会有时间间隔,我们想要的效果是三个promise会判断上一个异步执行完成之后再去执行下一个异步
//就算使用for of 三个promise也是立即执行不会有时间间隔
let f=fn();
for(let i of f){
console.log(i)
}
- 将上例改造为会判断上一个异步执行完成之后再去执行下一个异步
co(fn);
function co(fn){
//f是fn()的执行结果
let f = fn();
next();
function next(data){
//调用f.next方法
let result = f.next();
// console.log(result)
//结果为{value:Promise,done:false}
//result.done为false的时候
if(!result.done){
//第一次的时候{value:Promise,done:false}中的Promise是第一个Promise对象
//这个Promise对象中有then方法
// 上一个异步走完了,再执行下一个异步
// 用了then当中info有resolve()传递的信息
result.value.then((info)=>{
console.log(info,data);
next(info);
//再去调用next()方法再次触发f.next()
//next()
});
}
}
}