JS 迭代器与生成器

迭代器(Iterator)

Iterator 的作用

  1. 为各种数据结构,提供一个统一的、简便的访问接口。
  2. 使得数据结构的成员能够按某种次序排列。
  3. ES6提供了新的遍历命令for…of循环来消费Iterator 接口。
  4. 任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。(即依次处理该数据结构的所有成员。)

Iterator 的遍历过程

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

Symbol.iterator属性

  1. ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性。
    (或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。)

  2. 原生具备 Iterator 接口的数据结构
    ① Array
    ② Map
    ③ Set
    ④ String
    ⑤ TypedArray(类型化的数组)
    ⑥ 函数的 arguments 对象
    ⑦ NodeList 对象

  3. 一个对象如果要具备可被for…of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。

实例
// 数组类的迭代器
let arr = ["a", "b", "c"];
let iter = arr[Symbol.iterator]();
console.log(iter.next()); // {value: "a", done: false}
console.log(iter.next()); // {value: "b", done: false}
console.log(iter.next()); // {value: "c", done: false}
console.log(iter.next()); // {value: undefined, done: true}
// iterator接口的自定义类
class RangeIterator {
  constructor(start, stop, step) {
    this.value = start;
    this.stop = stop;
    this.step = step;
  }
  [Symbol.iterator]() {
    return this;
  }
  next() {
    let value = this.value;
    if (value < this.stop) {
      this.value += this.step;
      return { done: false, value: value };
    }
    return { done: true, value: undefined };
  }
}

let range = (start, stop, step = 1) => {
  return new RangeIterator(start, stop, step);
};
for (let value of range(0, 9, 2)) {
  console.log(value); // 0 2 4 6 8;
}
// 为对象添加iterator接口
let obj = {
  data: ["a", "b", "c"],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return { value: self.data[index++], done: false };
        } else {
          return { value: undefined, done: true };
        }
      },
    };
  },
};
for (let d of obj) {
  console.log(d); // a b c;
}
console.log(obj[Symbol.iterator]); // [Function: [Symbol.iterator]]

生成器

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

let obj = {
  name1: "Tom",
  name2: "Jerry",
  name3: "Mickey",
  name4: "Miney",
  *[Symbol.iterator]() {
    // yield this.name1;
    // yield this.name2;
    // yield this.name3;
    // yield this.name4;
    // 注释的代码可以优化成下面一个for循环
    for (let i = 1; i < 5; i++) {
      yield this["name" + i];
    }
  },
};
console.log(obj[Symbol.iterator]().next()); // { value: 'Tom', done: false }
console.log([...obj]); // [ 'Tom', 'Jerry', 'Mickey', 'Miney' ]
for (let k of obj) {
  console.log(k); // Tom Jerry Mickey Miney;
}

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

遍历器对象的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()); // { value: 'hello', done: false }
console.log(hw.next()); // { value: 'world', done: false }
console.log(hw.next()); // { value: 'ending', done: true }
console.log(hw.next()); // { value: undefined, done: true }

实例:用生成器实现斐波那契数列

class Fib {
  constructor(num) {
  // num限制了输出的斐波那契数列最后一项的数值大小
    this.num = num;
  }
  // 这里用生成器对斐波那契数列进行输出
  *[Symbol.iterator]() {
    // 用解构赋值对数列第一、二项进行赋值
    let [a, b] = [0, 1];
    // 用while死循环进行遍历
    while (true) {
      // 斐波那契数列:该项等于前两项相加的和
      [a, b] = [b, a + b];
      // 当该项数值大于指定的num数值时,退出循环
      if (a > this.num) return;
      // 返回此时a的值,即小于num的最后一个数
      yield a;
    }
  }
}
let fibs = new Fib(100);
for (let f of fibs) {
  console.log(f); // 1 1 2 3 5 8 13 21 34 55 89
}
// 该类不足:由于num形参在类外部是暴露出来的,在调用时,可以在外部对num进行修改
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值