迭代协议

js/Iteration_protocols2026-10-29 11:03:47

迭代协议 具体分为两个协议:可迭代协议和迭代器协议。可迭代协议规定了什么是可迭代对象。迭代器协议规定了什么是迭代器对象。

可迭代对象 意味着内置方法或者自定义方法可以从这个对象上获取一个迭代器,使用返回的迭代器可以完成一次对可迭代对象的迭代。

迭代器 是一个对象,它定义一个序列,并在终止时可能附带一个返回值。

可迭代协议

可迭代协议 允许 JavaScript 对象定义或定制它们的 迭代行为,例如,在一个 for..of 结构中,哪些值可以被遍历到。一些内置类型同时是内置的可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object)。

可迭代对象

要成为可迭代对象,该对象必须实现 [Symbol.iterator]() 方法,这意味着对象(或者它原型链上的某个对象)必须有一个键为 [Symbol.iterator] 的属性,可通过常量 Symbol.iterator 访问该属性。

[Symbol.iterator]() 方法

Symbol.iterator 属性是一个无参数的函数,其返回值为一个符合迭代器协议的对象(迭代器对象,Iterator)。

值得注意的是调用此无参数函数时,它将作为对可迭代对象的方法进行调用。因此,在函数内部,this 关键字可用于访问可迭代对象的属性,以决定在迭代过程中提供什么。

此函数可以是普通函数,也可以是生成器函数,以便在调用时返回迭代器对象。在此生成器函数的内部,可以使用 yield 提供每个条目。

迭代器协议

迭代器协议 定义了产生一系列值(无论是有限个还是无限个)的标准方式,当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。

迭代器对象(Iterator)

迭代器对象 是一个具有 next 方法的对象。

next 方法是一个无参数或者接受一个参数的函数,返回一个实现 IteratorResult 接口的对象。

如果在使用迭代器内置的语言特征(例如 for...of)时,调用 next 方法得到一个非对象返回值(例如 false 或 undefined),将会抛出 TypeError("iterator.next() returned a non-object value")。

next 方法可以接受一个值,该值将提供给方法体。任何内置的语言特征都将不会传递任何值。传递给生成器 next 方法的值将成为相应 yield 表达式的值。

IteratorResult 接口

实现 IteratorResult 接口的对象包含两个属性:value 和 done。

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

实际上,两者都不是严格要求的;如果返回没有任何属性的对象,则实际上等价于 { done: false, value: undefined }。

如果一个迭代器返回一个 done: true 的结果,则对任何 next() 的后续调用也返回 done: true,尽管这在语言层面不是强制的。

如果 next() 返回的结果始终不是 done: true,那么一些内置迭代过程会陷入死循环,例如 for ... of。

迭代器对象的其他方法

可选地,迭代器可以实现 return(value) 和 throw(exception) 方法,这些方法在调用时告诉迭代器,调用者已经完成迭代,并且可以执行任何必要的清理(例如关闭数据库连接)。

return(value):无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的对象,其 value 通常等价于传递的 value,并且 done 等于 true。调用这个方法表明迭代器的调用者不打算调用更多的 next(),并且可以进行清理工作。

throw(exception):无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的对象,通常 done 等于 true。调用这个方法表明迭代器的调用者监测到错误的状况,并且 exception 通常是一个 Error 实例。

可迭代对象如何被使用

当一个对象需要被迭代的时候(比如被置入一个 for...of 循环时),首先,会不带参数调用它的 [Symbol.iterator]() 方法获得一个新的迭代器,然后使用迭代器获得要迭代的值。

通过调用迭代器的 next 方法来获取每次迭代的值,以及是否还有值。

内置的可迭代对象

String、Array、TypedArray、Map、Set 以及 Intl.Segments 都是内置的可迭代对象,因为它们的每个 prototype 对象都实现了 [Symbol.iterator]() 方法。此外,arguments 对象和一些 DOM 集合类型,如 NodeList 也是可迭代的。目前,没有内置的异步可迭代对象。

// 数组
const array = [1, 2, 3];
for (const value of array) {
  console.log(value); // 输出: 1, 2, 3
}

// 字符串
const string = "Hello";
for (const char of string) {
  console.log(char); // 输出: H, e, l, l, o
}

// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
  console.log(key, value); // 输出: a 1, b 2
}

// Set
const set = new Set([1, 2, 3]);
for (const value of set) {
  console.log(value); // 输出: 1, 2, 3
}

内置的使用可迭代对象的场景

有许多 API 接受可迭代对象。一些例子,包括:

  • Map()
  • WeakMap()
  • Set()
  • WeakSet()
  • Promise.all()
  • Promise.allSettled()
  • Promise.race()
  • Promise.any()
  • Array.from()
  • Object.groupBy()
  • Map.groupBy()
const set = new Set([1, 2, 3]);
const array = Array.from(set);
console.log(array); // 输出: [1, 2, 3]

一些语句和表达式期望可迭代对象,例如 for...of 循环、数组和参数扩展(展开语法)、yield* 和数组解构:

// 数组解构
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 输出: 1 2 3
// 数组和参数扩展(展开语法)
const array = [1, 2, 3];
const newArray = [...array, 4, 5];
console.log(newArray); // 输出: [1, 2, 3, 4, 5]

手动使用可迭代对象

const iterable = [1, 2, 3]; // 数组是一个可迭代对象
const iterator = iterable[Symbol.iterator](); // 创建一个迭代器

console.log(iterator.next()); // 输出 {value: 1, done: false}
console.log(iterator.next()); // 输出 {value: 2, done: false}
console.log(iterator.next()); // 输出 {value: 3, done: false}
console.log(iterator.next()); // 输出 {value: undefined, done: true}

自定义可迭代对象

以下是一个自定义可迭代对象的示例:

// iterable 是可迭代对象,可迭代对象定义了 [Symbol.iterator]() 方法
const iterable = {
  [Symbol.iterator]() {
    let step = 0;
    // [Symbol.iterator]() 返回了一个迭代器对象,迭代器对象定义了 next() 方法
    return {
      next() {
        step++;
        if (step === 1) {
          return { value: 'Hello', done: false };
        } else if (step === 2) {
          return { value: 'World', done: false };
        }
        // next() 方法返回了 IteratorResult 对象,表明每次迭代的状态
        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of iterable) {
  console.log(value); // 输出: Hello, World
}
// 当 iterable 被迭代时,首先调用 [Symbol.iterator]() 方法生成一个迭代器对象
// 然后调用迭代器对象的 next() 方法获取 IteratorResult,根据 value 获取迭代值,根据 done 判断是否继续

自定义可迭代对象使用实现 return 方法的迭代器:

const iterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 3) {
          return { value: step, done: false };
        }
        return { value: undefined, done: true };
      },
      return() {
        console.log('Cleanup');
        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of iterable) {
  console.log(value); // 输出: 1, 2
  if (value === 2) break; // 提前终止迭代
}
// 输出: Cleanup

自定义可迭代对象使用实现 throw 方法的迭代器,需要手动迭代:

const iterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 3) {
          return { value: step, done: false };
        }
        return { value: undefined, done: true };
      },
      throw(error) {
        console.log('Error:', error);
        return { value: undefined, done: true };
      }
    };
  }
};

const iterator = iterable[Symbol.iterator]();

try {
  console.log(iterator.next()); // 输出: { value: 1, done: false }
  console.log(iterator.next()); // 输出: { value: 2, done: false }
  iterator.throw(new Error('Something went wrong')); // 输出: Error: Error: Something went wrong
} catch (e) {
  console.error(e);
}

参考

迭代器和生成器
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_generators

迭代协议
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols

本页目录