ES6学习笔记(四):for...of循环

ES6引入了for...of循环,作为遍历所有数据结构的统一方式。只要一个数据结构部署了Symbol.iterator属性,就被视为拥有Iterator接口,就可以用for...of遍历。
for...of循环可以遍历数组、Set和Map结构、类似数组的对象、Generator对象以及字符串。
注意,for…of不能遍历对象,因为对象没有iterator接口,对象可以使用for…in遍历。

遍历数组

数组原生就具有Iterator接口,可以直接使用for…of遍历。

1
2
3
4
let arr = ['apple', 'orange', 'pear'];
for(let value of arr) {
console.log(value); // apple orange pear
}

上面代码的遍历方式只能获得数组的值,不能获得索引,如果要遍历索引可以使用数组的entries或者keys方法。

1
2
3
4
5
6
7
let arr = ['apple', 'orange', 'pear'];
for(let [key, value] of arr.entries()) {
console.log(key, value);
// 0 "apple"
// 1 "orange"
// 2 "pear"
}

遍历Set和Map结构

在前面的“Set和Map数据结构”已经详细讲过使用for...of遍历Set和Map结构了,用法也很简单。

1
2
3
4
5
6
7
8
9
10
11
12
let set = new Set(['apple', 'orange', 'pear']);
for(let value of set) {
console.log(value); // apple orange pear
}
let map = new Map([['apple', 'like'], ['orange', 'dislike'], ['pear', 'like']]);
for(let item of map) {
console.log(item);
// ["apple", "like"]
// ["orange", "dislike"]
// ["pear", "like"]
}

上面代码中分别演示了for...of遍历Set和Map结构,需要注意的一点是Set结构默认是调用values方法,返回键值的遍历器。而Map结构默认是调用entries方法,返回键值对的遍历器。

遍历类似数组的对象

类似数组的对象有很多种,下面是使用for...of遍历字符串、arguments对象以及DOM NodeList对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 字符串
let str = 'hello';
for(let value of str) {
console.log(value); // h e l l o
}
// arguments对象
function add() {
for(let value of arguments) {
console.log(value);
}
}
add(1, 3, 4, 5); // 1 3 4 5
// DOM NodeList对象
let divs = document.querySelectorAll('div');
for(let div of divs) {
div.classList.add('test'); // 为每一个div添加一个‘test’类
}

并不是所有类似数组的对象都具有iterator接口,例如下面例子中的likeArray具有length属性,而且键名是数字,对于这种特殊的对象,可以使用Array.from将其先转为数组,然后遍历。

1
2
3
4
5
6
7
8
9
10
let likeArray = {length: 4, 1: 'b', 3: 'd'};
// 报错,因为对象没有iterator接口
for(let value of likeArray) {
console.log(value);
}
for(let value of Array.from(likeArray)) {
console.log(value); // undefined b undefined d
}

JavaScript遍历方法的比较

我们以数组为例,JavaScript提供了多种方法遍历数组,常见的有forforEachfor...in以及for...of,下面我们来依次比较一下这几种方法的不同之处。
for循环是我们比较熟悉的

1
2
3
4
5
6
7
8
9
10
let arr = [1, 3, 5, 6, 7];
for(let i=0; i<arr.length; i++) {
if(i === 2) {
continue;
}
if(i > 3) {
break;
}
console.log(arr[i]); // 1 3 6
}

for循环的好处就是能够使用continuebreakreturn等命令,而短处就是写起来比较麻烦,所有数组内置了forEach方法。

1
2
3
4
5
6
7
8
let arr = [1, 3, 5];
arr.forEach((value, key) => {
value += 2;
console.log(key, value);
// 0 3
// 1 5
// 2 7
})

forEach内部是一个回调函数,可以对每个成员执行某种操作,但是无法中途跳出循环continuebreak命令无法生效。
再来看for...infor...in只能遍历数组的键名,而且它还有几个弊端。

1
2
3
4
5
6
7
8
9
10
11
let arr = [1, 3];
for(let key in arr) {
console.log(key); // 0 1
console.log(typeof key); // string string
console.log(arr[key]); // 1 3
}
arr.name = '张三'; // 添加一个非数字键
for(let key in arr) {
console.log(key); // 0 1 name
console.log(arr[key]); // 1 3 张三
}

从上面的代码中我们可以看出for...in的几个弊端:
1、数组的键名是数字,但是for...in循环是一字符串作为键名’0’、’1’、’2’等
2、for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键
3、某些情况下,for...in会以任意顺序遍历键名
总而言之,不要使用for...in遍历数组,它只适合用来遍历对象
for...of相对于上面的几种遍历方法有以下优点
1、和for...in一样的简洁语法,但是没有for...in的那些缺点
2、不同于forEach方法,for...of中可以使用continuebreak命令
3、提供了遍历所有数据结构的统一接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let arr = [1, 2, 3, 4, 5, 6, 7, 8];
for(let value of arr) {
if(value % 2) {
continue;
}
if(value === 6) {
break;
}
console.log(value); // 2 4
}
arr.name = 9; // 添加一个非数字键名
for(let key of arr.keys()) {
console.log(key); // 0 1 2 3 4 5 6 7
}

从上面的代码可以看出,for...of循环中可以使用continuebreak循环控制命令,而且不会遍历非数字键名

参考链接
ECMAScript 6 入门(阮一峰):Iterator和for…of循环

坚持原创技术分享,您的支持将鼓励我继续创作!