• ES6生成器(Generator)和迭代器(Iterator)


    引言

    我们常常需要遍历一个对象,例如访问数组里的值,字符串中的值,arguments里的值等。我么常用let...of...来对对象进行遍历,而这个方法的底层实现就是迭代器(Iterator)

    迭代器

    ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。而迭代器的本质,就是内部实现了一个next()方法,返回不同的数据。

    let arr = [1, 2 ,3]
    for(let item of arr){
      console.log(arr);
    } // 可以正常输出,1, 2, 3
    
    let obj = {name: 'huang'}
    for(let item of obj){
      console.log(item);
    } // 不可以正常输出,报错:obj is not iterable
    // 可以在控制台打印一下,查看二者是否具备[Symbol.iterator]属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    迭代器的使用

    获取具体的迭代器对象,然后调用迭代器身上的next()方法,就可以逐步拿到数据

    let arr = [1, 2, 3]
    console.log(arr);
    let Iter = arr[Symbol.iterator]() 
    console.log(Iter.next()); // {value: 1, done: false}
    console.log(Iter.next()); // {value: 2, done: false}
    console.log(Iter.next()); // {value: 3, done: false}
    console.log(Iter.next()); // {value: undefined, done: true}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    自定义迭代器

    总结:只要给一个对象实现了Symbol.iterator方法,let...of...就可以迭代。这个迭代的本质,就是借用了里面的next()方法来实现。

    class MyArray {
      constructor() {
        for (let i = 0; i < arguments.length; i++) {
          this[i] = arguments[i];
        }
        this.length = arguments.length;
      }
      [Symbol.iterator]() {
        let index = 0;
        let that = this;
        return {
          next: function () {
            return index < that.length ? 
              {value: that[index++], done: false}:
              {value: undefined, done: true}
          }
    
        }
      }
    }
    
    let arr = new MyArray(1, 2, 3);
    let Iter = arr[Symbol.iterator]()
    console.log(Iter.next());
    console.log(Iter.next());
    console.log(Iter.next());
    console.log(Iter.next());
    
    for(let item of arr){
      console.log(item); // 1, 2, 3
    }
    
    // 如果知识让你实现next方法,就不用自己定义数组类了,直接定义一个构造函数即可。
    
    function MyIterator(array){
      var idx = 0;
      return {
        next(){
          return idx < array.length ? 
                {value: array[idx++], done: false} :
                {value: undefined, done: true} 
        }
      }
    }
    
    let Iter = MyIterator([1, 2, 3, 4, 5]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    把对象转化为可迭代对象

    let obj = {
     name: "lnj",
      age: 34,
      gender: "man",
      [Symbol.iterator]() {
        let keys = Object.keys(this);
        let index = 0;
        let that = this;
        return {
          next() {
            return index < keys.length ?
              { value: that[keys[index++]], done: false } :
              { value: undefined, done: true }
          }
        }
      }
    }
    let it = obj[Symbol.iterator]();
    console.log(it.next());
    console.log(it.next());
    console.log(it.next());
    console.log(it.next());
    for (let value of obj) {
      console.log(value); // 'lnj', 34, 'man' 当然返回什么可以自己定义
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    类数组转化为迭代器

    let obj = {
      0: 1,
      1: 2,
      2: 3,
      length: 3,
      [Symbol.iterator]: Array.prototype[Symbol.iterator]
    }
    for (let item of obj) {
      console.log(item); // 1 2 3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意:let...of..., 解构赋值, 拓展运算符都是Iterator的应用场景,因为他们都需要迭代对象的内容

    生成器

    Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数内部可以封装多个状态, 因此又可以理解为是一个状态机。只需要在普通函数的function后面加上*,该函数就会变为Generator 函数,通过yield定义状态,并且yield关键字可以让 Generator内部的逻辑能够切割成多个部分。yield可以通过调用迭代器的next函数,不断执行定义的状态

    生成器可以定义多个状态,在不同的时机调用
    生成器可以返回迭代器对象,可以用来简写迭代器的写法

    基本用法

    每一次调用迭代器的next方法,都会调用相应的yield声明的状态前的代码片段。
    如果写了return,也是如此,只不过,后面的yield从此都不会被使用到了

    function* test(){
       const res1 = yield 1;
       console.log(res1); // two
       const res2 = yield 2;
       console.log(res2); // three
       const res3 =yield 3;
       console.log(res3); // four
       
     }
    
     let iter = test();
     console.log(iter.next('one'));
     console.log(iter.next('two'));
     console.log(iter.next('three'));
     console.log(iter.next('four'));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    返回多个结果

    function* test(a, b) {
      yield a + b
      yield a - b
    }
    let Iter = test(1, 2)
    
    console.log(Iter.next());  // {value: 3, done: false}
    console.log(Iter.next());  // {value: -1, done: false}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    用生成器使对象可遍历

    上面把对象转化为可迭代对象是利用自己定义的next方法。但是,生成器本来就是用来生成迭代器的,所以可以使上面的写法更加简单

    let obj = {
     name: "lnj",
      age: 34,
      gender: "man",
      [Symbol.iterator]: function* () {
        let index = 0;
        let keys = Object.keys(this)
        while(index < keys.length) {
          yield this[keys[index++]]
        }
        
      }
    }
    let it = obj[Symbol.iterator]();
    console.log(it.next()); // {value: 'lnj', done: false}
    console.log(it.next()); // {value: 34, done: false}
    console.log(it.next()); // {value: 'man', done: false}
    console.log(it.next()); // {value: undefined, done: true}
    for (let value of obj) {
      console.log(value); // 'lnj', 34, 'man' 当然返回什么可以自己定义
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Promise Generator async/await解决异步问题简要记录

    问题的出现来自于回调地狱

    function request(fn) {
      setTimeout(() => {
        fn('拿到的数据') // 发起了异步请求,拿到了数据,作为了fn的回调参数
      }, 3000);
    }
    
    request(function (data) {
      console.log('我是第一层', data);
      request(function (data) {
        console.log('我是第二层', data);
        request(function (data) {
          console.log('我是第三层', data);
        })
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Promise改写

    看起来规整了一点

    function request(){
     return new Promise((resolve, reject) => {
        setTimeout(() => resolve('拿到的数据'), 1000)
      })
    }
    
    request().then((data)=>{
      console.log('我是第一层', data);
      return request()
    }).then((data)=>{
      console.log('我是第二层', data);
      return request()
    }).then((data)=>{
      console.log('我是第三层', data);
      return request()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Generator改写

    Generator也是ES6提出的解决异步编程的方案,和Promise不属于同一技术(我目前认为)

     function request(fn) {
       setTimeout(() => {
         fn('拿到的数据') // 发起了异步请求,拿到了数据,作为了fn的回调参数
       }, 3000);
     }
    
     function* test(){
       yield request(function (data) {console.log('我是第一层', data);})
       yield request(function (data) {console.log('我是第二层', data);})
       yield request(function (data) {console.log('我是第三层', data);})
     }
    
     let Iter = test();
     // 三秒后这三个会同时出现, 但是我就是可以控制它什么时候有,什么时候没有
     Iter.next()
     Iter.next()
     Iter.next()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Async/Await

    简单理解:是Generator的语法糖, async替代*, await 替代yield, 并对其他的一些细节做了优化,例如Async函数的返回值是Promise包装过的,而Generator返回对象是Iterator。注意:await后如果异常了,需要自己去try...catch...,而Promise提供了catch方法,且Promise提供了更多的方法,例如Promise.all等

    这种写法让人看起来最最舒服了!!!!

    function request() {
      return new Promise(function (resolve, reject) {
        setTimeout(function () {
          resolve(2000);
        },1000)
      })
    
      // 当然也是可以不用return Promise对象的。。。会帮你转化为Promise
      // return 1
    
    }
    async function test() {
      let res1 = await request();
      console.log('第一层数据', res1);
      let res2 = await request();
      console.log('第二层数据', res2);
    
      let res3 = await request();
      console.log('第三层数据', res3);
    
    
    }
    
    let res = test()
    console.log(res) // 返回的是Promise;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    Spring事务什么时候会失效?
    linux相关指令
    go语言基本操作---三
    CRC校验原理及步骤
    GDAL Dataset.WriteRaster_Direct
    OpenLayers-要素属性信息简单弹窗
    Linux 文件操作命令
    164. 最大间距
    Python 协程详解
    go-zero&go web集成redis实战
  • 原文地址:https://blog.csdn.net/mianmami/article/details/126203755