for..of循环

伴随着我们熟知的JavaScriptforfor..in循环,ES6增加了一个for..of循环,它循环遍历一组由一个 迭代器(iterator) 产生的值。

你使用for..of循环遍历的值必须是一个 可迭代对象(iterable),或者它必须是一个可以被强制转换/封箱(参见本系列的 类型与文法)为一个可迭代对象的值。一个可迭代对象只不过是一个可以生成迭代器的对象,然后由循环使用这个迭代器。

让我们比较for..offor..in来展示它们的区别:

  1. var a = ["a","b","c","d","e"];
  2. for (var idx in a) {
  3. console.log( idx );
  4. }
  5. // 0 1 2 3 4
  6. for (var val of a) {
  7. console.log( val );
  8. }
  9. // "a" "b" "c" "d" "e"

如你所见,for..in循环遍历数组a中的键/索引,而for.of循环遍历a中的值。

这是前面代码段中for..of的前ES6版本:

  1. var a = ["a","b","c","d","e"],
  2. k = Object.keys( a );
  3. for (var val, i = 0; i < k.length; i++) {
  4. val = a[ k[i] ];
  5. console.log( val );
  6. }
  7. // "a" "b" "c" "d" "e"

而这是一个ES6版本的非for..of等价物,它同时展示了手动迭代一个迭代器(见第三章的“迭代器”):

  1. var a = ["a","b","c","d","e"];
  2. for (var val, ret, it = a[Symbol.iterator]();
  3. (ret = it.next()) && !ret.done;
  4. ) {
  5. val = ret.value;
  6. console.log( val );
  7. }
  8. // "a" "b" "c" "d" "e"

在幕后,for..of循环向可迭代对象要来一个迭代器(使用内建的Symbol.iterator;参见第七章的“通用Symbols”),然后反复调用这个迭代器并将它产生的值赋值给循环迭代的变量。

在JavaScript标准的内建值中,默认为可迭代对象的(或提供可迭代能力的)有:

  • 数组
  • 字符串
  • Generators(见第三章)
  • 集合/类型化数组(见第五章)

警告: 普通对象默认是不适用于for..of循环的。因为他们没有默认的迭代器,这是有意为之的,不是一个错误。但是,我们不会进一步探究这其中微妙的原因。在第三章的“迭代器”中,我们将看到如何为我们自己的对象定义迭代器,这允许for..of遍历任何对象来得到我们定义的一组值。

这是如何遍历一个基本类型的字符串中的字符:

  1. for (var c of "hello") {
  2. console.log( c );
  3. }
  4. // "h" "e" "l" "l" "o"

基本类型字符串"hello"被强制转换/封箱为等价的String对象包装器,它是默认就是一个可迭代对象。

for (XYZ of ABC)..中,XYZ子句既可以是一个赋值表达式也可以是一个声明,这与forfor..in中相同的子句一模一样。所以你可以做这样的事情:

  1. var o = {};
  2. for (o.a of [1,2,3]) {
  3. console.log( o.a );
  4. }
  5. // 1 2 3
  6. for ({x: o.a} of [ {x: 1}, {x: 2}, {x: 3} ]) {
  7. console.log( o.a );
  8. }
  9. // 1 2 3

与其他的循环一样,使用breakcontinuereturn(如果是在一个函数中),以及抛出异常,for..of循环可以被提前终止。在任何这些情况下,迭代器的return(..)函数(如果存在的话)都会被自动调用,以便让迭代器进行必要的清理工作。

注意: 可迭代对象与迭代器的完整内容参见第三章的“迭代器”。