[关闭]
@Dale-Lin 2019-09-27T15:02:06.000000Z 字数 2480 阅读 711

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

深入理解ES6


什么是迭代器

迭代器是一中特殊对象,专为迭代过程设计有专有API,所有迭代器都有一个 next() 方法,每次调用都返回一个结果对象。
结果对象有两个属性——value, done,分别表示返回的值和是否完成。

生成器

生成器用来返回一个迭代器:

  1. function *createIterator(items) {
  2. for (let i = 0; i < items.length; i++) {
  3. yield items[i];
  4. }
  5. return 'Done!';
  6. }
  7. let iterator = createIterator([1, 2, 3]);
  8. console.log(iterator.next()); //"{value: 1, done: false}"
  9. console.log(iterator.next()); //"{value: 2, done: false}"
  10. console.log(iterator.next()); //"{value: 3, done: false}"
  11. // 最后返回函数返回值
  12. console.log(iterator.next()); //"{value: 'Done!', done: true}"
  13. // 之后所有调用都会返回 undefined
  14. console.log(iterator.next()); //"{value: undefied, done: true}"

function 关键字后用 * 表示它是一个生成器;yield 关键字用来指定调用迭代器的 next() 方法时的返回值和返回顺序。
每当执行完一条 yield 语句就会停止,直到下次调用 next() 方法继续。

yield 关键字只能在生成器内部使用,就像一个 return 关键字一样,不能穿透函数边界。

有以下生成器表达式语法:

  1. let createIterator = function *(items) {
  2. //code
  3. }
  1. let o = {
  2. *createIterator(items) {
  3. //code
  4. }
  5. }
  6. let iterator = o.createIterator([1, 2, 3]);

yield* 语句表明后面是一个可迭代对象,也可以是另一个生成器:

  1. function* g4() {
  2. yield* [1,2,3]
  3. return 'foo'
  4. }
  5. var result
  6. function* g5() {
  7. result = yield* g4()
  8. }
  9. var iterator = g5()

可迭代对象和 for-of 循环

数组、Set、Map、字符串是可迭代对象,具有 Symbol.iterator 方法,可以使用 for-of 循环。
for-of 循环每执行一次都会调用可迭代对象的 next() 方法,并将迭代器返回的结果对象的 value 属性储存在一个变量中,循环将持续执行到返回兑现的 done 属性值为 true

  1. let values = [1, 2, 3];
  2. //调用可迭代对象的 Symbol.iterator 方法返回默认迭代器
  3. let iterator = values[Symbol.iterator]();
  4. console.log(iterator.next()); //"{value: 1, done: false}"
  5. console.log(iterator.next()); //"{value: 2, done: false}"
  6. console.log(iterator.next()); //"{value: 3, done: false}"
  7. console.log(iterator.next()); //"{value: undefined, done: true}"

默认情况下,开发者定义的对象都不是可迭代对象,但如果给 Symbol.iterator 属性添加一个生成器函数,则可将其变成可迭代对象(可使用 for-of):

  1. const collection = {
  2. items: [],
  3. *[Symbol.iterator]() {
  4. for (let item of this.items) {
  5. yield item;
  6. }
  7. }
  8. };
  9. collection.items.push(1);
  10. collection.items.push(2);
  11. collection.items.push(3);
  12. for (let x of collection) {
  13. console.log(x);
  14. }
  15. //1
  16. //2
  17. //3

Object.keys(obj)

返回一个对象的可枚举属性的键名组成的数组:

  1. let b = {
  2. name: 'Nicholas',
  3. age: 10,
  4. };
  5. Object.keys(b); // ['name', 'age']

Object.values(obj)

返回一个对象的可枚举属性的值组成的数组:

  1. let b = {
  2. name: 'Nicholas',
  3. age: 10,
  4. };
  5. Object.values(b); // ['Nicholas', 20]

Object.entries(obj)

返回由一个对象的可枚举属性的键值对数组所组成的数组:

  1. let b = {
  2. name: 'Nicholas',
  3. age: 10,
  4. };
  5. Object.entries(b); // [['name', 'Nicholas'], ['age', 10]]

使用 new Map(Object.entries(obj)) 可以快速将 Object 转化成 Map。

Object.fromEntries(arr)

反操作,将一个键值对对象(entries 数组或map)转化为对象:

  1. const map = new Map([['foo', 'bar'], ['baz', 10]]);
  2. const obj = Object.fromEntries(map);
  3. // {foo: 'bar', baz: 10}

不同集合类型的默认迭代器

每个集合类型都有一个默认的迭代器,在 for-of 循环中,如果没有显式指定,则使用默认的迭代器。

数组和 Set 集合默认迭代器是 values(),Map 集合则是 entries()。

在 entries() 返回的数组键值对中可以使用解构。

字符串迭代器

使用 for-of 循环优化字符串的迭代,以全面支持 Unicode。

NodeList 迭代器

迭代 NodeList 也可以使用 for-of 循环,其行为和数组的默认迭代器完全一样。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注