ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of
循环,作为遍历所有数据结构的统一的方法。
一个数据结构只要部署了Symbol.iterator
属性,就被视为具有 iterator 接口,就可以用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法。
for...of
循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments
对象、DOM NodeList 对象)、Generator 对象,以及字符串。
数组
数组原生具备iterator
接口(即默认部署了Symbol.iterator
属性),可以使用for...of循环
。
const arr = [1, 3, 5, 7, 9];
for (let item of arr) {
console.log(item) // 1 3 5 7 9
}
上面代码表明for...of
循环读取键值。如果要通过for...of
循环,获取数组的索引,可以借助数组实例的entries
方法和keys
方法。
获取键名
const arr = [1, 3, 5, 7, 9]
for (let v of arr.keys()) {
console.log(v) // 0 1 2 3 4
}
获取键名和键值
const arr = [1, 3, 5, 7, 9]
for(let [index, item] of arr.entries()) {
console.log(index, item)
}
/*
0 1
1 3
2 5
3 7
4 9
*/
Set和Map结构
Set 和 Map 结构也原生具有 Iterator 接口,可以直接使用for...of
循环。
const engines = new Set([1, 3, 5, 3, 5]) // Set结构会去重
for (let v of engines) {
console.log(v)
}
// 1
// 3
// 5
// Map
const es6 = new Map()
es6.set('a', 6)
es6.set('b', 8)
for (let [name, value] of es6) {
console.log(name + ": " + value);
}
// a: 6
// b: 8
类似数组的对象
类似数组的对象包括好几类。下面是for...of
循环用于字符串、DOM NodeList 对象、arguments
对象的例子。
// 字符串
let str = 'hello'
for (let v of str) {
console.log(v) // h e l l o
}
// DOM NodeList对象
let paras = document.querySelectorAll("div")
for (let div of paras) {
div.classList.add("test")
}
// arguments对象
function printArgs() {
for (let x of arguments) {
console.log(x)
}
}
printArgs('a', 'b')
// 'a'
// 'b'
对象
对于普通的对象,for...of
结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。
遍历对象可使用for...in循环。或者使用Object.keys
方法将对象的键名生成一个数组,然后遍历这个数组。
let obj = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"
};
for (let e in obj) {
console.log(e);
}
// edition
// committee
// standard
// 使用Object.keys方法
for (let key of Object.keys(obj)) {
console.log(obj[key]);
}
// 6
// TC39
// ECMA-262
与其他遍历语法的比较
以数组为例,JavaScript 提供多种遍历语法。最原始的写法就是for
循环。
let arr = [1, 3, 5]
for (let i=0; i< arr.length; i++) {
console.log(arr[i])
}
这种写法比较麻烦,因此数组提供内置的forEach
方法。
let arr = [1, 3, 5]
arr.forEach(item => {
console.log(item)
})
这种写法的问题在于,无法中途跳出forEach
循环,break
命令或return
命令都不能奏效。
for...in
循环可以遍历数组的键名。
let arr = [1, 3, 5]
for (let index in arr) {
console.log(arr[index])
}
for...in
循环有几个缺点。
- 数组的键名是数字,但是
for...in
循环是以字符串作为键名“0”、“1”、“2”等等。 -
for...in
循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。 - 某些情况下,
for...in
循环会以任意顺序遍历键名。
总之,for...in
循环主要是为遍历对象而设计的,不适用于遍历数组。
for...of
循环相比上面几种做法,有一些显著的优点。
- 有着同
for...in
一样的简洁语法,但是没有for...in
那些缺点。 - 不同于
forEach
方法,它可以与break
、continue
和return
配合使用。 - 提供了遍历所有数据结构的统一操作接口。