Iterator和Generator

Iterator 和 Generator

Iterator(迭代器)

详细文档:ECMAScript 6 入门-Iterator 和 for...of 循环

Iterator(迭代器)的概念

Iterator 即遍历器对象

Generator 函数调用后可以返回一个遍历器对象 Iterator

所有可以使用 for of 遍历的对象内部都实现时了 Iterator 接口 比如:

通过Arrary.prototype[Symbol.iterator]()可以返回 Iterator

{// Arrary 内部的遍历器对象
    __proto__: Array Iterator{
        next: ƒ next()			// 调用 next 方法将 指针指向下一个元素
        Symbol(Symbol.toStringTag): "Array Iterator"
        __proto__: Object
    }
}

当然也可直接实现一个遍历器对象

var it = makeIterator(["a", "b"]);

it.next(); // { value: "a", done: false }
it.next(); // { value: "b", done: false }
it.next(); // { value: undefined, done: true }

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function () {
      return nextIndex < array.length
        ? { value: array[nextIndex++], done: false }
        : { value: undefined, done: true };
    },
  };
}

调用 next 方法, 返回{ value: xxx, done: boolean },通过判断 done 是否为 true,来判断遍历是否结束

调用 Iterator 接口的场合

有一些场合会默认调用 Iterator 接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,还有几个别的场合。

(1)解构赋值

对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。

let set = new Set().add("a").add("b").add("c");

let [x, y] = set;
// x='a'; y='b'

let [first, ...rest] = set;
// first='a'; rest=['b','c'];

(2)扩展运算符

扩展运算符(...)也会调用默认的 Iterator 接口。

// 例一
var str = "hello";
[...str]; //  ['h','e','l','l','o']

// 例二
let arr = ["b", "c"];
["a", ...arr, "d"];
// ['a', 'b', 'c', 'd']

上面代码的扩展运算符内部就调用 Iterator 接口。

实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。

let arr = [...iterable];

(3)yield*

yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

let generator = function* () {
  yield 1;
  yield* [2, 3, 4];
  yield 5;
};

var iterator = generator();

iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: 4, done: false }
iterator.next(); // { value: 5, done: false }
iterator.next(); // { value: undefined, done: true }

(4)其他场合

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

  • for...of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]])
  • Promise.all()
  • Promise.race()

Generator(生成器)

详细文档:ECMAScript 6 入门-Generator 函数的语法

建议看:JavaScript 高级程序设计(第 4 版)中关于生成器的描述

基本概念

Generator 函数 是生成器函数,Generator 函数 返回 Generator(生成器)

Generator 是一个状态机,封装了多个内部状态(本人的理解:是一个可以控制内部执行步骤的 函数)

Generator 生成器 内部实现了 Iterator (迭代器)接口,因此具有 next() 方法。调用这个方法会让生

成器开始或恢复执行

控制内部执行步骤

  • 调用 generatorFn 生成器函数的时候并不会执行内部代码

  • generator 调用 next 方法, 开始执行 generatorFn 内部代码

  • 遇到 yield 关键字将停止执行代码,并将 yield 后面的值返回

  • 再次调用 next 方法 将从上一个 yield 所在行开始执行

  • 如果 next 中有参数将传递给开始执行所在行的 yield,如果是首次调用 next 将没有 yield 可以供其赋值

  • 遇到 return 关键字将返回

  • 如果函数内没有 return 也没有 yield 的了 将返回

正常执行顺序

function* generatorFn() {
  console.log("0->yield1");
  yield "yield1"; // 停止 返回 yield1
  console.log("yield1->yield2");
  yield "yield2"; // 停止 返回 yield2
  console.log("yield2->end");
}

const generator = generatorFn();
console.log("next1", generator.next());
// 0->yield1
// next1 { value: 'yield1', done: false }
console.log("next2", generator.next());
// yield1->yield2
// next2 { value: 'yield2', done: false }
console.log("next3", generator.next());
// yield2->end
// next3 { value: undefined, done: true }

next 传参

function* generatorFn(params) {
  console.log(params);
  console.log("yield1", yield "yield1"); // 遇到 yield ,yield所在行的代码都不会执行,但是 yield 后边的参数会返回
  console.log("yield2", yield "yield2"); // 不会执行console.log,下次调用next时才会执行console.log以及后面的代码
  console.log("end");
}

const generator = generatorFn("foo");

// ***第一次的bar并不会赋值给yield1关键字 因为这一次调用是为了开始执行生成器函数,第二次调用yield1的值变成baz***
console.log("next1", generator.next("bar"));
// foo
// next1 { value: 'yield1', done: false }
console.log("next2", generator.next("baz"));
// yield1 baz
// next2 { value: 'yield2', done: false }
console.log("next3", generator.next("qux"));
// yield2 qux
// end
// next3 { value: undefined, done: true }

带 return 的

function* helloWorldGenerator() {
  yield "hello";
  yield "world";
  return "ending";
}

var hw = helloWorldGenerator();

hw.next();
// { value: 'hello', done: false }

hw.next();
// { value: 'world', done: false }

hw.next();
// { value: 'ending', done: true }

hw.next();
// { value: undefined, done: true }
posted @ 2021-10-11 11:02  __Bowen  阅读(29)  评论(0编辑  收藏  举报