一、可迭代性

如果一个对象实现了 Symbols.iterator 属性的时候,就可以因为这个对象是可迭代的。一些内置的类型如 ArrayMapSetStringIne32ArrayUnit32Array 等都实现了 Symbold.iterator。对象上的 Symbol.iterator 函数负责返回供迭代的值。

for ... of 语句

for...of 会遍历可迭代的对象,调用对象上的 Symbol.iterator 方法。下面是在数组上使用 for..of 的简单例子:

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

for...offor...in 语句

for...offor...in 均可迭代一个列表;但是用于迭代的值是不同的,for...in 迭代的是对象的 key,而 for...of 迭代的是对象的 value

下面上面两种不同的迭代的方式的对比:

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...of 是 ES6 的新特性,并且无法直接操作(普通)对象,而 for...in 则是可以操作对象,是查看对象属性的一种方法。但是 for...of 关注迭代对象的值。

内置对象 MapSet 已经实现了 Symbol.iterator 方法,让我们能访问其保存的值。

let pets = new Set(["Cat", "Dog"]);
pets['species'] = "mammals";

for (let pet in pets) {
    console.log(pet); // 'species'
}
for (let pet of pets) {
    console.log(pet); // 'cat' 'dog'
}

1.jpg

上面代码在 TypeScript 中是报错的,因为类型约束的关系。不过这不重要,需要注意的是,因为 for...of 是 ES6 语法,因此编译器会将其转为 ES5 的语法。但是 Set 是 API,因此也不会转换。

以下面例子举例:

如果目标是 ES5 或者 ES3 环境,编译器会进行转换

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

编译器转换完之后:

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

2.jpg

如果目标是 ES6 环境,编译器会生成相应引擎的 for...of 内置迭代器实现方式