迭代器, 生成器

状态: 初稿

可迭代对象

如果一个对象实现了 Symbol.iterator 属性, 就被认为是可迭代的.
有些内建类型如 Array, Map, Set, String, Int32Array, Uint32Array 等已实现了它们的 Symbol.iterator 属性.
对象的 Symbol.iterator 函数的职责是返回要迭代的列表.

for..of 语句

for..of 调用一个可迭代对象的 Symbol.iterator 属性, 遍历它每个元素.
for..of 遍历一个数组:

1
2
3
4
5
let someArray = [1, "string", false];

for (let entry of someArray) {
console.log(entry); // 1, "string", false
}

for..of 对比 for..in

for..offor..in 都迭代一个列表; 但是迭代的值不一样, for..in 返回被迭代对象的列表, 而 for..of 返回被迭代对象所有数值属性的列表.

下例显示该差别:

1
2
3
4
5
6
7
8
9
let list = [4, 5, 6];

for (let i in list) {
console.log(i); // "0", "1", "2",
}

for (let i of list) {
console.log(i); // "4", "5", "6"
}

另一点差别是 for..in 操作任何对象; 提供的是查看该对象属性的方法.
for..of 则不同, 它主要关注可迭代对象储存的值. MapSet 等内建对象实现 Symbol.iterator 属性来访问它们储存的值.

1
2
3
4
5
6
7
8
9
10
let pets = new Set(["Cat", "Dog", "Hamster"]);
pets["species"] = "mammals";

for (let pet in pets) {
console.log(pet); // "species"
}

for (let pet of pets) {
console.log(pet); // "Cat", "Dog", "Hamster"
}

代码生成

面向 ES5 和 ES3

当面向 ES5 或 ES3 兼容引擎, 只有 Array 类型的值允许迭代器.
for..of 遍历非 Array 值, 即使这些值实现了 Symbol.iterator 属性, 也是错误的.

编译器会为 for..of 循环生成简单 for 循环, 例如:

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

会被转换为:

1
2
3
4
5
var numbers = [1, 2, 3];
for (var _i = 0; _i < numbers.length; _i++) {
var num = numbers[_i];
console.log(num);
}

面向 ECMAScript 2015 及更高版本

如果面向 ECMAScript 2015 兼容引擎, 编译器生成 for..of 以利用引擎内建的迭代器实现.