深入理解ES6之迭代器与生成器

迭代器

迭代器是被设计专用于迭代的对象,带有特定接口。所有的迭代器对象都拥有next()方法,会返回一个结果对象。该结果对象有两个属性:对应下一个值的value,以及一个布尔类型的done,其值为true时表示没有更多值可供使用。迭代器持有一个指向集合位置的内部指针,每当调用了next()方法,迭代器就会返回相应的下一个值。

使用es5模拟一个迭代器

function createIterator(items) {
  var i = 0
  // 返回一个迭代器对象
  return {
    next: () => {
      var done = i >= items.length
      var value = !done ? items[++i] : undefined
      return {
        done,
        value
      }
    }
  }
}

var list = createIterator([1, 2, 3, 4, 5])
console.log(list.next())    // { done: false, value: 2 }
console.log(list.next())    // { done: false, value: 3 }
console.log(list.next())    // { done: false, value: 4 }
console.log(list.next())    // { done: false, value: 5 }
console.log(list.next())    // { done: true, value: undefined }

生成器

生成器(generator)是能返回一个迭代器的函数。生成器函数由放在function关键字之后的一个星号(*)来表示,并能使用新的yield关键字。将星号紧跟在function关键字之后,或是在中间留出空格,都是没问题的。

function *createIterator() {
  yield 1
  yield 2
  yield 3
}

let iterator = createIterator()

console.log(iterator.next().value)    // 1
console.log(iterator.next().value)    // 2
console.log(iterator.next().value)    // 3

可迭代对象与 for-of 循环

与迭代器紧密相关的是,可迭代对象(iterable)是包含Symbol.iterator属性的对象。这个Symbol.iterator知名符号定义了为指定对象返回迭代器的函数。在 ES6 中,所有的集合对象(数组、 Set 与 Map )以及字符串都是可迭代对象,因此它们都被指定了默认的迭代器。

for-of循环在循环每次执行时会调用可迭代对象的next()方法,并将结果对象的value值存储在一个变量上。循环过程会持续到结果对象的done属性变成true为止。

let value = [1, 2, 3]
for (let num of value) {
  console.log(num)
}

输出:

1
2
3

访问默认迭代器 Symbol.iterator

let values = [1, 2, 3, 4, 5]
let iterator = values[Symbol.iterator]()
iterator.next()    // {value: 1, done: false}
iterator.next()    // {value: 2, done: false}
iterator.next()    // {value: 3, done: false}

集合的迭代器

ES6 具有三种集合对象类型:数组、 Map 与 Set 。

  • entries():返回一个包含键值对的迭代器;
  • values():返回一个包含集合中的值的迭代器;
  • keys():返回一个包含集合中的键的迭代器。
let map = new Map([['name', 'age'], ['map', 5]])
map.entries()    // MapIterator {"name" => "age", "iterator" => 5, "friends" => "none"}
map.values()    // MapIterator {"map", 5}
map.keys()    // MapIterator {"name", "age"}

let set = new Set([1, 2, 3, 4, 5])
set.entries()    // SetIterator {1 => 1, 2 => 2, 3 => 3}
set.values()    // SetIterator {1, 2, 3, 4, 5}
set.keys()    // SetIterator {1, 2, 3, 4, 5}

let arr = [1, 2, 3, 4, 5]
arr.entries()    // Array Iterator {}
arr.values()    // Array Iterator {}
arr.keys()    // Array Iterator {}

调用迭代器方法返回的数据需要通过 for...of... 来遍历

字符串的迭代器

从 ES5 发布开始, JS 的字符串就慢慢变得越来越像数组。例如 ES5 标准化了字符串的方括号表示法,用于访问其中的字符(即:使用text[0]来获取第一个字符,以此类推)。

所以也可以像遍历集合那样遍历字符串

  • 传统 for 循环
  • for...of...

NodeList 的迭代器

文档对象模型( DOM )具有一种NodeList类型,用于表示页面文档中元素的集合。NodeList对象与数组都使用了length属性来标明项的数量,并且都使用方括号表示法来访问各个项。然而本质上来说,NodeList与数组的行为是完全不同的,这会引发许多混乱。
着默认迭代器被附加到 ES6 , DOM 关于NodeList的规定也包含了一个默认迭代器(此规定在 HTML 规范而非 ES6 规范中),其表现方式与数组的默认迭代器一致。这意味着你可以将NodeList用于for-of循环,或用于其他使用对象默认迭代器的场合。

let divs = document.getElementsByTagName("div")
for (let div of divs) {
  console.log(div.id)
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。