大量 code 警告
从一个数据集合中按照一定的顺序,不断取出数据的过程
迭代强调的是依次取数据,并不保证取多少,也不保证把所有的数据取完 遍历强调的是要把整个数据依次全部取出
对迭代过程的封装,在不同的语言中有不同的表现形式,通常为对象
一种设计模式,用于统一迭代过程,并规范了迭代器规格:
JS规定,如果一个对象具有next方法,并且该方法返回一个对象,该对象的格式如下:
1{value: 值, done: 是否迭代完成}模板
1const obj = {
2 next() {
3 return {
4 value:xxx,
5 done:xx
6 }
7 }
8}则认为该对象obj是一个迭代器 含义:
演示1
1const arr = [1, 2, 3, 4, 5];
2//迭代数组arr
3const iterator = {
4 i: 0, //当前的数组下标
5 next() {
6 var result = {
7 value: arr[this.i],
8 done: this.i >= arr.length//下标越界了
9 }
10 this.i++;
11 return result;
12 }
13}
14//让迭代器不断的取出下一个数据,直到没有数据为止
15let data = iterator.next();
16while (!data.done) { //只要没有迭代完成,则取出数据
17 console.log(data.value)
18 //进行下一次迭代
19 data = iterator.next();
20}
21
22console.log("迭代完成")演示2
1const arr1 = [1, 2, 3, 4, 5];
2const arr2 = [6, 7, 8, 9];
3// 迭代器创建函数 iterator creator
4function createIterator(arr) {
5 let i = 0;//当前的数组下标
6 return {
7 next() {
8 var result = {
9 value: arr[i],
10 done: i >= arr.length
11 }
12 i++;
13 return result;
14 }
15 }
16}
17const iter1 = createIterator(arr1);
18iter1.next()
19const iter2 = createIterator(arr2);
20iter2.next()演示3:无限延伸功能
1// 依次得到斐波拉契数列前面n位的值
2// 1 1 2 3 5 8 13 .....
3//创建一个斐波拉契数列的迭代器
4function createFeiboIterator() {
5 let prev1 = 1,
6 prev2 = 1, //当前位置的前1位和前2位
7 n = 1; //当前是第几位
8 return {
9 next() {
10 let value;
11 if (n <= 2) {
12 value = 1;
13 } else {
14 value = prev1 + prev2;
15 }
16 const result = {
17 value,
18 done: false//这个数列无限延伸
19 };
20 prev2 = prev1;
21 prev1 = result.value;
22 n++;
23 return result;
24 }
25 }
26}
27const iterator = createFeiboIterator();
28iterator.next()可迭代协议
ES6规定,如果一个对象具有知名符号属性Symbol.iterator,并且属性值是一个迭代器创建函数,则该对象是可迭代的(iterable)
数组就是个可迭代对象
思考:如何知晓一个对象是否是可迭代的?答案:数组、类数组、自己写的对象都可以做成可迭代对象
思考:如何遍历一个可迭代对象?答案:for-of循环
for-of 循环用于遍历可迭代对象,格式如下
1//迭代完成后循环结束
2for(const item in iterable){
3 //iterable:可迭代对象
4 //item:每次迭代得到的数据
5}for-of循环的原理
调用对象的[Symbol.iterator]方法,得到一个迭代器。不断调用next方法,只有返回的done为false,则将返回的value传递给变量,然后进入循环体执行一次。
演示:数组就是个可迭代对象
1const arr = [5, 7, 2, 3, 6];
2// const iterator = arr[Symbol.iterator]();
3// let result = iterator.next();
4// while (!result.done) {
5// const item = result.value; //取出数据
6// console.log(item);
7// //下一次迭代
8// result = iterator.next();
9// }
10// 等价于
11for (const item of arr) {
12 console.log(item)
13}演示3
1<div>1</div>
2<div>2</div>
3<div>3</div>
4<script>
5 const divs = document.querySelectorAll("div");
6 // const iterator = divs[Symbol.iterator]()
7 // let result = iterator.next();
8 // while (!result.done) {
9 // const item = result.value; //取出数据
10 // console.log(item);
11 // //下一次迭代
12 // result = iterator.next();
13 // }
14 // 等价于
15 for (const item of divs) {
16 console.log(item);
17 }
18</script>演示:自定义可迭代对象
1//可迭代对象
2var obj = {
3 a: 1,
4 b: 2,
5 [Symbol.iterator]() {
6 const keys = Object.keys(this);//得到对象this里面所有属性名的一个数组
7 // console.log(keys)
8 let i = 0;
9 return {
10 next: () => {
11 const propName = keys[i];
12 const propValue = this[propName];
13 const result = {
14 value: {
15 propName,
16 propValue
17 },
18 done: i >= keys.length
19 }
20 i++;
21 return result;
22 }
23 }
24 }
25}
26for (const item of obj) {
27 console.log(item); // {propName:"a", propValue:1}
28}展开运算符可以作用于可迭代对象,这样,就可以轻松的将可迭代对象转换为数组。 演示
1var obj = {
2 a: 1,
3 b: 2,
4 [Symbol.iterator]() {
5 const keys = Object.keys(this);
6 let i = 0;
7 return {
8 next: () => {
9 const propName = keys[i];
10 const propValue = this[propName];
11 const result = {
12 value: {
13 propName,
14 propValue
15 },
16 done: i >= keys.length
17 }
18 i++;
19 return result;
20 }
21 }
22 }
23}
24
25const arr = [...obj];
26console.log(arr);
27
28function test(a, b) {
29 console.log(a, b)
30}
31
32test(...obj);解决副作用的 redux 中间件
redux-thunk:需要改动action,可接收action是一个函数
redux-promise:需要改动action,可接收action是一个promise对象,或action的payload是一个promise对象
以上两个中间件,会导致action或action创建函数不再纯净。
redux-saga是建立在ES6的生成器基础上的,要熟练的使用saga,必须理解生成器。
要理解生成器,必须先理解迭代器和可迭代协议。
生成器是一个通过构造函数Generator创建的对象,生成器既是一个迭代器(有next方法),同时又是一个可迭代对象(有知名符号),可以用于for of循环
1//伪代码
2
3var generator = new Generator();
4generator.next();//它具有next方法
5var iterator = generator[Symbol.iterator];//它也是一个可迭代对象
6for(const item of generator){
7 //由于它是一个可迭代对象,因此也可以使用for of循环
8}注意:Generator构造函数,不提供给开发者使用,仅作为JS引擎内部使用
生成器的创建,必须使用生成器函数(Generator Function)
1//这是一个生成器函数,该函数一定返回一个生成器
2function* method(){
3}第一次调用只是生成了生成器,并没有调用里面 生成器函数内部是为了给生成器的每次迭代提供的数据 每次调用生成器的next方法,将导致生成器函数运行到下一个yield关键字位置 yield是一个关键字,该关键字只能在生成器函数内部使用,表达“产生”一个迭代数据。
1). 生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中 2). 调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值 3). 第一次调用next方法时,传参没有任何意义 4). 在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号
1generator.return()//结束了,done为true
2generator.return(3)//value=31generator.throw(new Error("sdbjjas"))演示
1function* test() {
2 console.log("第1次运行")
3 yield 1;
4 console.log("第2次运行")
5 yield 2;
6 console.log("第3次运行")
7}
8
9const generator = test();优化演示:数组的迭代
1const arr1 = [1, 2, 3, 4, 5];
2const arr2 = [6, 7, 8, 9];
3
4// 迭代器创建函数 iterator creator
5function* createIterator(arr) {
6 for (const item of arr) {
7 yield item;
8 }
9}
10
11const iter1 = createIterator(arr1);
12const iter2 = createIterator(arr2);创建一个斐波拉契数列的迭代器
1function* createFeiboIterator() {
2 let prev1 = 1,
3 prev2 = 1, //当前位置的前1位和前2位
4 n = 1; //当前是第几位
5 while (true) {
6 if (n <= 2) {
7 yield 1;
8 } else {
9 const newValue = prev1 + prev2
10 yield newValue;
11 prev2 = prev1;
12 prev1 = newValue;
13 }
14 n++;
15 }
16}
17
18const iterator = createFeiboIterator();演示:生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中
1function* test() {
2 console.log("第1次运行")
3 yield 1;
4 console.log("第2次运行")
5 yield 2;
6 console.log("第3次运行");
7 return 10;
8}
9
10const generator = test();演示:调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值
1function* test() {
2 console.log("函数开始")
3 let info = yield 1;//这里info不是1,这里根据参数动态变化
4 console.log(info)
5 info = yield 2 + info;
6 console.log(info)
7}
8const generator = test();
9控制台
10generator.next()
11demo.js:2 函数开始
12{value: 1, done: false}
13generator.next(5)
14{value: 7, done: false}演示:在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号
1function* t1(){
2 yield "a"
3 yield "b"
4}
5function* test() {
6 两种错误的调用方式:
7 1. t1()// 调用生成器函数不会导致里面的代码执行
8 2. yield t1();// 将生成器对象给了yield执行
9 yield* t1();//相当于把t1里面代码直接copy过来了
10 yield 1;
11 yield 2;
12 yield 3;
13}
14const generator = test();生成器
1// generator.next === generator[Symbol.iterator]().next()
2//下面的函数是一个生成器函数,用于创建生成器
3function* createGenerator() {
4 console.log("生成器函数的函数体 - 开始");
5 yield 1; // {value:1, done:false}
6 console.log("生成器函数的函数体 - 运行1");
7 yield 2;
8 console.log("生成器函数的函数体 - 运行2");
9 yield 3;
10 console.log("生成器函数的函数体 - 运行3");
11 return "结束";
12}
13
14var generator = createGenerator(); //调用后,一定得到一个生成器1function* createArrayIterator(arr) {
2 for (let i = 0; i < arr.length; i++) {
3 const item = arr[i];
4 console.log(`第${i}次迭代`);
5 yield item;
6 }
7 console.log("函数结束");//迭代完成后运行
8}
9
10var generator = createArrayIterator([1, 2, 3, 4, 5, 6]);1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 yield 1; // {value:1, done:false}
5 return;// 后面不会运行了
6 console.log("生成器函数的函数体 - 运行1");
7 yield 2;
8 console.log("生成器函数的函数体 - 运行2");
9 yield 3;
10 console.log("生成器函数的函数体 - 运行3");
11 return "结束";
12}
13
14var generator = createGenerator(); //调用后,一定得到一个生成器1function asyncGetData() {
2 return new Promise(resolve => {
3 setTimeout(() => {
4 resolve("成哥")
5 }, 2000);
6 })
7}
8
9function* task() {
10 console.log("开始获取数据....");
11 const data = yield asyncGetData()
12 console.log("获取到数据:", data);
13 const data2 = yield asyncGetData();
14 console.log("又获取到了数据:", data2);
15 const data3 = yield 1;
16 console.log("又获取到了数据:", data3)
17}
18
19/**
20 * 通用函数:运行一个生成器任务
21 */
22function run(generatorFunction) {
23 const generator = generatorFunction(); //得到一个生成器
24 next();
25
26 /**
27 * 封装了generator的next方法,进行下一次迭代
28 */
29 function next(nextValue) {
30 const result = generator.next(nextValue);
31 if (result.done) {
32 //迭代结束了
33 return;
34 }
35 const value = result.value; //拿到迭代的数据
36 if (typeof value.then === "function") {
37 //迭代的数据是一个Promise
38 value.then(data => next(data));
39 } else {
40 next(result.value);
41 }
42 }
43}
44
45run(task);1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 let result = yield 1; //将1作为第一次的迭代的值,还没有完成赋值 绝不是把1赋值给result,result是外部给他的
5 console.log("生成器函数的函数体 - 运行1", result);
6 result = yield 2; //将2作为第二次迭代的值
7 console.log("生成器函数的函数体 - 运行2", result);
8 result = yield 3;
9 console.log("生成器函数的函数体 - 运行3", result);
10 return "结束";
11}
12
13var generator = createGenerator(); //调用后,一定得到一个生成器
14
15var result = generator.next(); //{value:1, done:false}
16while (!result.done) {
17 //有迭代的值
18 result = generator.next(result.value);//如果想把yield返回的值交给result
19}1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 try {
4 console.log("生成器函数的函数体 - 开始");
5 let result = yield 1; //将1作为第一次的迭代的值.
6 console.log("生成器函数的函数体 - 运行1", result);
7 result = yield 2; //将2作为第二次迭代的值
8 console.log("生成器函数的函数体 - 运行2", result);
9 result = yield 3;
10 console.log("生成器函数的函数体 - 运行3", result);
11 return "结束";
12 } catch (err) {
13 console.log("报错了");
14 yield "Abc";
15 }
16
17}
18
19var generator = createGenerator(); //调用后,一定得到一个生成器1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 let result = yield 1; //将1作为第一次的迭代的值
5 console.log("生成器函数的函数体 - 运行1", result);
6 result = yield 2; //将2作为第二次迭代的值
7 console.log("生成器函数的函数体 - 运行2", result);
8 result = yield 3;
9 console.log("生成器函数的函数体 - 运行3", result);
10 return "结束";
11
12}
13
14var generator = createGenerator(); //调用后,一定得到一个生成器1function* g2() {
2 console.log("g2-开始");
3 let result = yield "g1";
4 console.log("g2-运行1");
5 result = yield "g2";
6 return 123;
7}
8//下面的函数是一个生成器函数,用于创建生成器
9function* createGenerator() {
10 console.log("生成器函数的函数体 - 开始");
11 let result = yield 1; //将1作为第一次的迭代的值
12 result = yield* g2(); //result为g2函数的返回值
13 console.log("生成器函数的函数体 - 运行1", result);
14 result = yield 2; //将2作为第二次迭代的值
15 console.log("生成器函数的函数体 - 运行2", result);
16 result = yield 3;
17 console.log("生成器函数的函数体 - 运行3", result);
18 return "结束";
19
20}
21
22var generator = createGenerator(); //调用后,一定得到一个生成器yield* 函数()调用生成器函数,则该函数的返回结果,为该表达式的结果1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 yield 1; // {value:1, done:false}
5 console.log("生成器函数的函数体 - 运行1");
6 yield 2;
7 console.log("生成器函数的函数体 - 运行2");
8 yield 3;
9 console.log("生成器函数的函数体 - 运行3");
10 return "结束";
11}
12
13var generator = createGenerator(); //调用后,一定得到一个生成器1function* createArrayIterator(arr) {
2 for (let i = 0; i < arr.length; i++) {
3 const item = arr[i];
4 console.log(`第${i}次迭代`)
5 yield item;
6 }
7 console.log("函数结束")
8}
9
10var generator = createArrayIterator([1, 2, 3, 4, 5, 6])1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 yield 1; // {value:1, done:false}
5 return;
6 console.log("生成器函数的函数体 - 运行1");
7 yield 2;
8 console.log("生成器函数的函数体 - 运行2");
9 yield 3;
10 console.log("生成器函数的函数体 - 运行3");
11 return "结束";
12}
13
14var generator = createGenerator(); //调用后,一定得到一个生成器1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 let result = yield 1; //将1作为第一次的迭代的值
5 console.log("生成器函数的函数体 - 运行1", result);
6 result = yield 2; //将2作为第二次迭代的值
7 console.log("生成器函数的函数体 - 运行2", result);
8 result = yield 3;
9 console.log("生成器函数的函数体 - 运行3", result);
10 return "结束";
11}
12
13var generator = createGenerator(); //调用后,一定得到一个生成器
14
15var result = generator.next(); //{value:1, done:false}
16while (!result.done) {
17 //有迭代的值
18 result = generator.next(result.value);
19}1function asyncGetData() {
2 return new Promise(resolve => {
3 setTimeout(() => {
4 resolve("成哥")
5 }, 2000);
6 })
7}
8
9function* task() {
10 console.log("开始获取数据....");
11 const data = yield asyncGetData()
12 console.log("获取到数据:", data);
13 const data2 = yield asyncGetData();
14 console.log("又获取到了数据:", data2);
15 const data3 = yield 1;
16 console.log("又获取到了数据:", data3)
17}
18
19/**
20 * 通用函数:运行一个生成器任务
21 */
22function run(generatorFunction) {
23 const generator = generatorFunction(); //得到一个生成器
24 next();
25
26 /**
27 * 封装了generator的next方法,进行下一次迭代
28 */
29 function next(nextValue) {
30 const result = generator.next(nextValue);
31 if (result.done) {
32 //迭代结束了
33 return;
34 }
35 const value = result.value; //拿到迭代的数据
36 if (typeof value.then === "function") {
37 //迭代的数据是一个Promise
38 value.then(data => next(data));
39 } else {
40 next(result.value);
41 }
42 }
43}
44
45run(task);1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 try {
4 console.log("生成器函数的函数体 - 开始");
5 let result = yield 1; //将1作为第一次的迭代的值
6 console.log("生成器函数的函数体 - 运行1", result);
7 result = yield 2; //将2作为第二次迭代的值
8 console.log("生成器函数的函数体 - 运行2", result);
9 result = yield 3;
10 console.log("生成器函数的函数体 - 运行3", result);
11 return "结束";
12 } catch (err) {
13 console.log("报错了");
14 yield "Abc";
15 }
16}
17
18var generator = createGenerator(); //调用后,一定得到一个生成器1//下面的函数是一个生成器函数,用于创建生成器
2function* createGenerator() {
3 console.log("生成器函数的函数体 - 开始");
4 let result = yield 1; //将1作为第一次的迭代的值
5 console.log("生成器函数的函数体 - 运行1", result);
6 result = yield 2; //将2作为第二次迭代的值
7 console.log("生成器函数的函数体 - 运行2", result);
8 result = yield 3;
9 console.log("生成器函数的函数体 - 运行3", result);
10 return "结束";
11}
12
13var generator = createGenerator(); //调用后,一定得到一个生成器1function* g2() {
2 console.log("g2-开始");
3 let result = yield "g1";
4 console.log("g2-运行1");
5 result = yield "g2";
6 return 123;
7}
8//下面的函数是一个生成器函数,用于创建生成器
9function* createGenerator() {
10 console.log("生成器函数的函数体 - 开始");
11 let result = yield 1; //将1作为第一次的迭代的值
12 result = yield* g2(); //result为g2函数的返回值
13 console.log("生成器函数的函数体 - 运行1", result);
14 result = yield 2; //将2作为第二次迭代的值
15 console.log("生成器函数的函数体 - 运行2", result);
16 result = yield 3;
17 console.log("生成器函数的函数体 - 运行3", result);
18 return "结束";
19
20}
21
22var generator = createGenerator(); //调用后,一定得到一个生成器高仿await
1function* task() {
2 const d = yield 1;
3 console.log(d)
4 // //d : 1
5 const resp = yield fetch("http://101.132.72.36:5100/api/local")
6 const result = yield resp.json();
7 console.log(result);
8}
9run(task)
10function run(generatorFunc) {
11 const generator = generatorFunc();
12 let result = generator.next(); //启动任务(开始迭代), 得到迭代数据
13 handleResult();
14 //对result进行处理
15 function handleResult() {
16 if (result.done) {
17 return; //迭代完成,不处理
18 }
19 //迭代没有完成,分为两种情况
20 //1. 迭代的数据是一个Promise
21 //2. 迭代的数据是其他数据
22 if (typeof result.value.then === "function") {//promise里面有个then方法
23 //1. 迭代的数据是一个Promise
24 //等待Promise完成后,再进行下一次迭代
25 result.value.then(data => {
26 result = generator.next(data)
27 handleResult();
28 })
29 } else {
30 //2. 迭代的数据是其他数据,直接进行下一次迭代
31 result = generator.next(result.value)
32 handleResult();
33 }
34 }
35}