迭代器与生成器

Iterator 的作用

为各种数据结构,提供一个统一的、简便的访问接口。

使得数据结构的成员能够按某种次序排列。

ES6提供了新的遍历命令for...of循环来消费Iterator 接口。

任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

Iterator 的遍历过程

  1. 创建一个指针对象(迭代器),指向当前数据结构的起始位置。 也就是说,迭代器对象本质上,就是一个指针对象。该对象必定有一个名为next的方法。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

Iterator接口

原生具备Iterator接口的数据结构:

  • Array
  • Map
  • Set
  • String
  • TypedArray (类型化的数组)
  • 函数的 arguments 对象
  • NodeList 对象

Symbol.iterator方法

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator方法。

let arr = ["a", "b", "c"];
let iter = arr[Symbol.iterator]();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
//输出
//{ value: 'a', done: false }
//{ value: 'b', done: false }
//{ value: 'c', done: false }
//{ value: undefined, done: true }

    一个对象如果要具备可被for...of调用的 Iterator 接口,就必须在Symbol.iterator上部署生成迭代器对象的方法。

    迭代器对象必须具备next方法。

    next方法必须返回一个具备value和done的对象。
            value为每次迭代的值,迭代结束时该值为undefined。
            done为布尔值,表示迭代是否结束,结束为true,否则为false。

    展开运算符...,也是消费的iterator接口。

    class Rander {
      constructor(start, stop, step) {
        this.start = start;
        this.stop = stop;
        this.step = step;
      }
      [Symbol.iterator]() {
        let range = this;
        return {
          next() {
            let value = range.value;
            if (value < range.stop) {
              range.value += range.step;
              return { done: false, value: value };
            }
            return { done: true, value: undefined };
          },
        };
      }
    }
    function range(start, stop, step = 1) {
      return new Rander(start, stop, step);
    }
    for (let i of range(0, 9, 2)) {
      console.log(i);
    }
    let num = [...range(0, 9, 2)];
    console.log(num);
    

    Generator函数

    通过Generator函数实现Iterator 接口迭代。

    let obj = {
      name1: "Tom",
      name2: "Jerry",
      name3: "Mickey",
      name4: "Miney",
      *[Symbol.iterator]() {
        yield this.name1;
        yield this.name2;
        yield this.name3;
      },
    };
    console.log(obj[Symbol.iterator]().next());
    console.log([...obj]);
    for (let k of obj) {
      console.log(k);
    }
    //输出
    //{ value: 'Tom', done: false }
    //[ 'Tom', 'Jerry', 'Mickey' ]
    //Tom
    //Jerry
    //Mickey

    迭代器对象的next方法的运行逻辑

    1. 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值作为返回的对象的value属性值。
    2. 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
    3. 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值作为返回的对象的value属性值。
    4. 如果该函数没有return语句(即没有返回值),则返回的对象的value属性值为undefined。

    迭代器对象除了具有next方法,还可以具有return方法。

    如果for...of循环提前退出(通常是因为出错,或者有break语句), 就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。

    function* helloWorldGenerator() {
      yield "hello";
      yield "world";
      return "ending";
    }
    let hw = helloWorldGenerator();
    console.log(hw.next());
    console.log(hw.next());
    console.log(hw.next());
    console.log(hw.next());
    //输出
    //{ value: 'hello', done: false }
    //{ value: 'world', done: false }
    //{ value: 'ending', done: true }
    //{ value: undefined, done: true }

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值