for..of
循环
伴随着我们熟知的JavaScriptfor
和for..in
循环,ES6增加了一个for..of
循环,它循环遍历一组由一个 迭代器(iterator) 产生的值。
你使用for..of
循环遍历的值必须是一个 可迭代对象(iterable),或者它必须是一个可以被强制转换/封箱(参见本系列的 类型与文法)为一个可迭代对象的值。一个可迭代对象只不过是一个可以生成迭代器的对象,然后由循环使用这个迭代器。
让我们比较for..of
与for..in
来展示它们的区别:
var a = ["a","b","c","d","e"];
for (var idx in a) {
console.log( idx );
}
// 0 1 2 3 4
for (var val of a) {
console.log( val );
}
// "a" "b" "c" "d" "e"
如你所见,for..in
循环遍历数组a
中的键/索引,而for.of
循环遍历a
中的值。
这是前面代码段中for..of
的前ES6版本:
var a = ["a","b","c","d","e"],
k = Object.keys( a );
for (var val, i = 0; i < k.length; i++) {
val = a[ k[i] ];
console.log( val );
}
// "a" "b" "c" "d" "e"
而这是一个ES6版本的非for..of
等价物,它同时展示了手动迭代一个迭代器(见第三章的“迭代器”):
var a = ["a","b","c","d","e"];
for (var val, ret, it = a[Symbol.iterator]();
(ret = it.next()) && !ret.done;
) {
val = ret.value;
console.log( val );
}
// "a" "b" "c" "d" "e"
在幕后,for..of
循环向可迭代对象要来一个迭代器(使用内建的Symbol.iterator
;参见第七章的“通用Symbols”),然后反复调用这个迭代器并将它产生的值赋值给循环迭代的变量。
在JavaScript标准的内建值中,默认为可迭代对象的(或提供可迭代能力的)有:
- 数组
- 字符串
- Generators(见第三章)
- 集合/类型化数组(见第五章)
警告: 普通对象默认是不适用于for..of
循环的。因为他们没有默认的迭代器,这是有意为之的,不是一个错误。但是,我们不会进一步探究这其中微妙的原因。在第三章的“迭代器”中,我们将看到如何为我们自己的对象定义迭代器,这允许for..of
遍历任何对象来得到我们定义的一组值。
这是如何遍历一个基本类型的字符串中的字符:
for (var c of "hello") {
console.log( c );
}
// "h" "e" "l" "l" "o"
基本类型字符串"hello"
被强制转换/封箱为等价的String
对象包装器,它是默认就是一个可迭代对象。
在for (XYZ of ABC)..
中,XYZ
子句既可以是一个赋值表达式也可以是一个声明,这与for
和for..in
中相同的子句一模一样。所以你可以做这样的事情:
var o = {};
for (o.a of [1,2,3]) {
console.log( o.a );
}
// 1 2 3
for ({x: o.a} of [ {x: 1}, {x: 2}, {x: 3} ]) {
console.log( o.a );
}
// 1 2 3
与其他的循环一样,使用break
,continue
,return
(如果是在一个函数中),以及抛出异常,for..of
循环可以被提前终止。在任何这些情况下,迭代器的return(..)
函数(如果存在的话)都会被自动调用,以便让迭代器进行必要的清理工作。
注意: 可迭代对象与迭代器的完整内容参见第三章的“迭代器”。