signed

QiShunwang

“诚信为本、客户至上”

迭代器和生成器

2021/3/21 1:28:27   来源:

目录

    • 迭代的理解
    • 迭代和遍历的区分
    • 迭代器
    • 迭代模式
    • js中的迭代器
    • 迭代数组
    • 迭代器创建函数
    • 斐波拉契数组
    • 可迭代对象
    • 遍历可迭代对象
    • es6 for-of循环
      • 自定义可迭代对象
      • 展开运算符与可迭代对象
    • 生成器(generator)
      • 生成器例子
      • 利用生成器改造之前的迭代器
      • 生成器函数需要注意的几个点
    • 生成器的应用(react中会用)

迭代的理解

把一堆数据按照一定的顺序拿出来的过程。再简单点就是从一堆东西中,先拿一个,再拿一个。

迭代和遍历的区分

迭代强调是:依次取数据,取多少,取不取完都不知道,取一个算一个。
遍历强调的是:要取完,即完整性。

产品迭代:这个产品最终是什么样的是不知道的,但是可以去做这产品,在过程中不断迭代更新。

迭代器

对迭代过程的封装。帮我们去拿数据的一个东西。通常表示为对象。

迭代模式

规定迭代器的功能。

  • 迭代器应该有得到下一个数据的能力。
  • 迭代器应该具有判断后面还有没有数据的能力。

js中的迭代器

如果一个对象具有next方法, 并且这个方法返回一个对象,对象的格式如下:

{ value: 值,done: 是否迭代完成}

代码如下:

const obj = {
	next(){
		return {
			value:"",
			done:boolean
		}
	}	
}

那么obj就是一个迭代器。

迭代数组

const arr = [1, 2, 3, 4, 5];
const iterator = {
    i: 0,
    next() {
        const 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();
}

迭代器创建函数

function getIterator(arr) {
    let i = 0;
    return {
        next() {
            const result = {
                value: arr[i],
                done: i >= arr.length
            }
            i++;
            return result;
        }
    }
}

斐波拉契数组

function createFriboIterator() {
    let temp1 = 1,
        temp2 = 1, //当前位的前两位
        n = 1; //当前是第几位
    return {
        next() {
            if (n <= 2) {
                value = 1
            } else {
                value = temp1 + temp2;
            }
            const result = {
                value,
                done: false
            }
            temp1 = temp2;
            temp2 = result.value;
            n++;
            return result;
        }
    }
}

const feibo = createFriboIterator();

可迭代对象

如果一个对象具有一个Symbol.iterator的属性名,其值为一个迭代器创建函数,那么这个对象就是可迭代对象。比如:

const obj = {
	[Symbol.iterator]:function(){
		return {
				next(){
					return {
							value:""
							done:""
						}
				}
		}
	}
}

es6中数组是一个可迭代对象
在这里插入图片描述

const arr = [1,2,3,4,5];
const iterator = arr[Symbol.iterator]();

在这里插入图片描述

  • 浏览器中将dom对象也封装成可迭代对象

遍历可迭代对象

const arr = [1,2,3,4,5];
 const iterator = arr[Symbol.iterator]();
 let item = iterator.next();
 while(!item.done){
     console.log(item.value);
     item = iterator.next();
 }

es6 for-of循环

for-of循环遍历可迭代对象,格式:

for(const item of iterable){
	item://每次迭代得到的对象
	iterable://可迭代对象
}
const arr = [1,2,3,4,5];
for(const item of arr){
	console.log(item);
}

等价于

const arr = [1,2,3,4,5];
const iterator = arr[Symbol.iterator]();
const item = iterator.next();
while(!item.done){
	console.log(itme.value);
	item = itertator.next();
}

自定义可迭代对象

 const obj = {
    a: 1,
     b: 2,
     [Symbol.iterator]() {
         const arr = Object.keys(this);
         let i = 0;
         return {
             next: () => {
                 const result = {
                     value: {
                         propName: arr[i],
                         propValue: this[arr[i]]
                     },
                     done: i >= arr.length
                 }
                 i++;
                 return result;
             }
         }
     }
 }
 for (const iterator of obj) {
     console.log(iterator); //
 }

在这里插入图片描述

展开运算符与可迭代对象

展开运算符可以作用于可迭代对象。这样就可以将可迭代对象转化为数组。

const obj = {
            a: 1,
            b: 2,
            [Symbol.iterator]() {
                const arr = Object.keys(this);
                let i = 0;
                return {
                    next: () => {
                        const result = {
                            value: {
                                propName: arr[i],
                                propValue: this[arr[i]]
                            },
                            done: i >= arr.length
                        }
                        i++;
                        return result;
                    }
                }
            }
        }
const arr = [...obj];
console.log(arr);

在这里插入图片描述

function test(a, b) {
            console.log(a);
            console.log(b);
        }
        // 第一次迭代的结果放在第一个参数,第二次迭代的结果放在第二个参数
        test(...obj);

在这里插入图片描述

生成器(generator)

生成器的出现是为了解决迭代器的编写复杂
生成器是一个迭代器,又是一个可迭代对象。

书写生成器

//这是一个生成器函数,该函数一定返回一个生成器。
function *method(){
	//这里面的代码要生成器迭代后才能运行,调用methed()生成器函数,只是得到一个生成器。
	//yield关键字 只能在生成器函数里面使用,给生成器提供迭代数据
	//生成器调用一次next()方法进行一次迭代,拿到yield关键字后面的数据
}

生成器例子

function *test(){
	console.log('第一次运行');
	yield 1;
	console.log('第二次运行');
	yield 2;
	console.log('第三次运行');
}
//调用一个生成器函数,得到一个生成器。这里调用函数test不会运行函数体里面的语句。
const generator = test();
//生成器进行迭代,运行到yield关键字,拿到迭代的数据
console.log(generator.next()); //第一次运行 {value: 1, done: false}
console.log(generator.next()); // 第二次运行 {value: 2, done: false}
console.log(generator.next()); // 第三次运行 {value: undefined, done: true}

在这里插入图片描述

利用生成器改造之前的迭代器

  • 循环数组
const arr = [1,2,3,4,5];
  function *createIterator(arr){
      for (const item of arr) {
           yield item;
      }
  }
  const generator = createIterator(arr);
  let item = generator.next();
  while(!item.done){
      console.log(item);
      item = generator.next();
  }
  • 斐波拉契函数
function* createFeibo() {
            let temp1 = 1,
                temp2 = 1,
                n = 1;
            while(true){
                if(n <= 2){
                    yield 1;
                } else {
                    let newValue = temp1 + temp2;
                    yield newValue;
                    temp1 = temp2;
                    temp2 = newValue;
                }
                n++;
            }
        }
const feibo = createFeibo();

生成器函数需要注意的几个点

  1. 函数的返回值,出现在第一次done为true的value里面
function *test(){
	console.log('第一次运行');
	yield 1;
	console.log('第二次运行');
	yield 2;
	console.log('第三次运行');
	return 100;
}

const generator = test();

console.log(generator.next()); //第一次运行 {value: 1, done: false}
console.log(generator.next()); // 第二次运行 {value: 2, done: false}
console.log(generator.next()); // 第三次运行 {value: 100, done: true}
console.log(generator.next()); // {value: undefined, done: true}
  1. 生成器调用next方法时,可以传递参数,参数会交给yield表达式的返回值
function *test(){
            let info = yield 1;
            console.log(info);
            info = yield 2 + info;
            console.log(info);
        }
const generator = test();
console.log(generator.next()); //{value:1,done:false}
console.log(generator.next(10)); // 10 {value:12,done:false}
console.log(generator.next()); // undefined {value:undefined,done:true}

分析:
在这里插入图片描述
3. 第一次通过next传递参数没有意义。没有用。
4. 生成器其它api
- return方法:调用该方法,提前结束生成器函数,提前结束迭代过程。

 function *test(){
                yield 1;
                yield 2;
                yield 3;
            }
            const generator = test();
            console.log(generator.next()); // {value:1,done:false}
            console.log(generator.return());//{value:undefined,done:true}
            console.log(generator.next()); // {value:undefined,done:true}

 function *test(){
                yield 1;
                yield 2;
                yield 3;
            }
            const generator = test();
            console.log(generator.next()); // {value:1,done:false}
            console.log(generator.return(100));//{value:100,done:true}
            console.log(generator.next()); // {value:undefined,done:true}
  • throw方法:调用该方法,可以在生成器中产生一个错误。
    在这里插入图片描述
    在这里插入图片描述
  1. 在生成器函数内部,可以调用其它生成器函数,但是要加上*号
function* test1(){
            yield 'a';
            yield 'b';
        } 
        function* test() {
            //得到是是生成器,没有迭代不会运行
            test1();
            yield 1;
            yield 2;
            yield 3;
        }
        const generator = test();
        console.log(generator.next()); //{value:1,done:false}
 function* test1(){
            yield 'a';
            yield 'b';
        } 
        function* test() {
            // value值为test1这个生成器
            yield test1();
            yield 1;
            yield 2;
            yield 3;
        }
        const generator = test();
        console.log(generator.next()); //{value:test1,done:false}
function* test1(){
            yield 'a';
            yield 'b';
        } 
        function* test() {
            // 正确的调用
            yield *test1();
            yield 1;
            yield 2;
            yield 3;
        }
const generator = test();
console.log(generator.next()); //{value:'a',done:false}

生成器的应用(react中会用)

promise出来后,async await还没出来,promise是es6.async await是es7
用promise来写还是有点繁琐,然后生成器是es6的,所以生成器这里可以简化promise。模拟async和await

 function* task() {
            const d = yield 1;
            console.log(d)
            // //d : 1
            const resp = yield fetch("url")
            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 (typeof result.value.then === "function") {
                    //1. 迭代的数据是一个Promise
                    //等待Promise完成后,再进行下一次迭代
                    result.value.then(data => {
                        result = generator.next(data)
                        handleResult();
                    })
                } else {
                    //2. 迭代的数据是其他数据,直接进行下一次迭代
                    result = generator.next(result.value)
                    handleResult();
                }
            }
        }