迭代的英文“iteration”源自拉丁文itero,意思是“重复”或“再来”。在软件开发领域,“迭代”的意思是按照顺序反复多次执行一段程序,通常会有明确的终止条件。 ECMAScript 6规范新增了两个高级特性:迭代器和生成器。使用这两个特性,能够更清晰、高效、方便地实现迭代。

1、理解迭代

在JavaScript中,计数循环就是一种最简单的迭代

for (let i = 1; i 迭代会在一个有序集合上进行,(“有序”可以理解为集合中所有项都可以按照既定的顺序被遍历到,特别是开始和结束项有明确的定义。)数组是JavaScript中有序集合的最典型例子。

let collection = ["foo", "bar", "baz"]; for (let index = 0; index 因为数组有已知的长度,且数组每一项都可以通过索引获取,所以整个数组可以通过递增索引来遍历。由于如下原因,通过这种循环来执行例程并不理想:

  1. 迭代之前需要事先知道如何使用数据结构。数组中的每一项都只能先通过引用取得数组对象,然后再通过[]操作符取得特定索引位置上的项。这种情况并不适用于所有数据结构。
  2. 遍历顺序并不是数据结构固有的。通过递增索引来访问数据是特定于数组类型的方式,并不适用于其他具有隐式顺序的数据结构。

ES5新增了Array.prototype.forEach()方法,向通用迭代需求迈进了一步,这个方法解决了单独记录索引和通过数组对象取得值的问题。因此这个方法只适用于数组,而且回调结构也比较笨拙。

2、迭代器

迭代器(iterator),是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节。

  • 其行为像数据库中的光标,迭代器最早出现在1974年设计的CLU编程语言中;
  • 在各种编程语言的实现中,迭代器的实现方式各不相同,但是基本都有迭代器,比如Java、Python等;

从迭代器的定义我们可以看出来,迭代器是帮助我们对某个数据结构进行遍历的对象。

在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol):迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式;在js中这个标准就是一个特定的next方法;

next方法有如下的要求: 一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象:

done(boolean)

  • 如果迭代器可以产生序列中的下一个值,则为 false。(这等价于没有指定 done 这个属性。)如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。

value

  • 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。

迭代器代码练习:

let names = ["sun", "james", "lebron"];

let index = 0;

let iterator = { next: function () { if (index let names = ["sun", "james", "lebron"];

function createArrayIterator(arr) { let index = 0; return { next: function () { if (index 可迭代对象但是上面的代码整体来说看起来是有点奇怪的:

  • 迭代一个数组的时候,需要自己创建一个index变量,再创建一个所谓的迭代器对象;
  • 事实上我们可以对上面的代码进行进一步的封装,让其变成一个可迭代对象;

当一个对象实现了可迭代协议时,它就是一个可迭代对象;这个对象的要求是必须实现 @@iterator 方法,在代码中我们使用 Symbol.iterator 访问该属性;

代码演示:

const iterableObj = {
  names: ["sun", "james", "lebron"],
  [Symbol.iterator]: function () {
    let index = 0;
    return {
      next: () => {
        if (index 转成这样的一个东西有什么好处呢?当一个对象变成一个可迭代对象的时候,进行某些迭代操作,比如 for…of 操作时,其实就会调用它的
@@iterator 方法;

原生迭代器对象平时创建的很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的:String、Array、Map、Set、arguments对象、NodeList集合;

可迭代对象的应用1、JavaScript中语法:for …of、展开语法(spread syntax)、yield*(后面讲)、解构赋值(Destructuring_assignment); 2、创建一些对象时:new Map([Iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable]); 3、一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable);

class ClassRoom {
  constructor(name, address, students) {
    (this.name = name), (this.address = address), (this.students = students);
  }
  [Symbol.iterator]() {
    let index = 0;

    return {
      next: () => {
        if (index 迭代器的中断迭代器在某些情况下会在没有完全迭代的情况下中断:比如遍历的过程中通过break、continue、return、throw中断了循环操作;比如在解构的时候,没有解构所有的值;想要监听中断的话,可以添加return方法:
class ClassRoom {
  constructor(name, address, students) {
    (this.name = name), (this.address = address), (this.students = students);
  }
  [Symbol.iterator]() {
    let index = 0;

    return {
      next: () => {
        if (index 3、生成器生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。

生成器函数也是一个函数,但是和普通的函数有一些区别: 1、生成器函数需要在function的后面加一个符号:* 2、生成器函数可以通过yield关键字来控制函数的执行流程 3、生成器函数的返回值是一个Generator(生成器),生成器事实上是一种特殊的迭代器

生成器函数执行

next方法是有返回值的:

{ value: undefined, done: false }如果不希望next方法返回这个对象,可以通过 yield value来实现。

生成器传递参数 – next函数在调用next函数的时候,可以给它传递参数,那么这个参数会作为上一个yield语句的返回值

function* say() {
  console.log(1);
  const n = yield;
  console.log(n);
}
const generator = say();
generator.next();
generator.next(2);
// 1
// 2生成器提前结束 – return函数还有一个可以给生成器函数传递参数的方法是通过return函数:return传值后这个生成器函数就会结束,之后调用next不会继续生成值了