for…of
1. 前言
在编程中最常见的就是对数据的遍历操作,ES5 有针对数组和对象的遍历方法,但这些方法或多或少地都会存在一定的问题。为了统一解决这些问题,ES6 给出了终极的解决方案 ——for...of
。
for...of
对于可迭代的对象(包括:内置的 String、Array、类似数组的对象(arguments 或 NodeList)、TypedArray、Map、Set,和用户定义的可迭代对象等)上创建一个迭代循环,它不局限于数组下的循环,只要是可迭代的对象就可以被 for...of
进行循环。
2. 基本语法
2.1 语法使用
for (const iterator of iterable) {
// 执行语句
}
参数解释:
参数 | 描述 |
---|---|
iterator |
在每次迭代中,将不同属性的值分配给变量,用于循环中语句的使用; |
iterable |
被迭代枚举的对象。 |
2.2 迭代 Array
for...of
最常用的场景就是对数组的迭代,也是取代 for
、forEach
的最好选择。
let arr = [10, 20, 30];
for (let value of arr) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
上面的代码中对 value 值进行加 1 操作,如果 value 值不能被修改,也可以使用 const
来定义 value。
2.3 迭代字符串
for...of
可以迭代字符串,迭代后的结果是把字符进行分割,得到每个单个字符。
let str = '慕课';
for (let value of str) {
console.log(value);
}
// 慕
// 课
2.4 迭代 TypedArray
let iterable = new Uint8Array([0x00, 0xff]);
for (let value of iterable) {
console.log(value);
}
// 0
// 255
2.5 迭代 Set 和 Map
在 Set 和 Map 章节中我们就说到了,Set 和 Map 可以使用 for...of
来进行循环,主要因为 Set 和 Map 具有可迭代属性。
let setArr = new Set([1, 1, 2, 2, 3, 3]);
for (let value of setArr) {
console.log(value);
}
// 1
// 2
// 3
上面的代码需要注意的是,迭代的是 new Set()
后的结果,new Set()
会对数组进行去重操作,所以得到以上结果。
let map = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (let value of map) {
console.log(value);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]
上面的代码中使用 new Map()
传入一个二维数组,这里需要注意的是,迭代的结果是一个带有 key 和 value 的数组,所以也可以用数组解构的方式 把 key 和 value 的值取出来,直接使用:
for (let [key, value] of map) {
console.log(key, value);
}
// a 1
// b 2
// c 3
2.6 迭代类数组对象
1. 迭代 argument 对象
我们知道在函数中可以使用 Argument
对象拿到在调用函数时拿到传递的参数,因为 arguments
不是一个 Array
,它属于类数组,可以借助 call
来得到一个数组。[].slice.call(arguments)
, 而使用 for...of
可以直接对 arguments
循环,得到的结果也只是传入的参数。这个可以很方便地去循环类数组对象。
function argfn() {
for (let argument of arguments) {
console.log(argument);
}
}
argfn(1,2,3)
// 1
// 2
// 3
上面的代码可以看出来,打印的结果只有 1、2、3
没有类数组上的其他属性值。
2. 迭代 DOM 集合
其实最常见的数组对象是得到网页上的 DOM 元素的集合,它也是一个类数组对象。比如一个 NodeList 对象:下面的例子演示给每一个 p 标签添加一个 “read” 类。
//注意:这只能在实现了NodeList.prototype[Symbol.iterator]的平台上运行
let prags = document.querySelectorAll("p");
for (let value of prags) {
value.classList.add("read");
}
上面的代码,需要在在带有 p 的标签的 html 文件中运行。
3 知识对比
ES5 中提供了很多遍历的方法,下面我们与之一一对比看看 for...of
有什么优势。
3.1 对比 for
最原始的语法是 for
循环语句,但是这种写法比较麻烦,每个步骤的信息都需要手动地去处理。
const fib = [1,1,2,3,5,8,13...]; // 斐波那切数列
for (let index = 0; index < fib.length; index++) {
console.log(fib[index]);
}
3.2 对比 forEach
数组中内置了 forEach
方法,这个方法的致命缺点就是不能跳出循环,break
命令和 return
命令都不能奏效。
fib.forEach((value) => {
console.log(value);
});
3.3 对比 for…in
for...in
用以遍历对象的属性,for...of
用以遍历数据,就像数组中的值一样;for...in
主要是针对对象循环而设计的,对于数组,键就是数字,但是在for...in
循环中是以字符串作为键名;for...in
循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键;- 某些情况下,
for...in
循环以任意顺序遍历键名,主要是因为对象在内存中的数据类型决定的。
for (let index in fib) {
console.log(fib[index]);
}
3.4 for...of
的优点
- 有着同
for...in
一样的简洁语法,但是没有for...in
那些缺点; - 不同于
forEach
方法,它可以与break
、continue
和return
配合使用; - 提供了遍历所有数据结构的统一操作接口。
for (let n of fib) {
if (n > 520)
break;
console.log(n);
}
当迭代项大于 520 时 break
语句会跳出 for...of
循环。
4. 小结
- 对于数组的处理尽量使用
for...of
去迭代数据; - 如果要遍历的是对象,并且没有顺序的限制可以使用
for...in
方式遍历对象更好的处理数据。