ES6 for…of循环
ES6新建了一种新的遍历命令for…of,作为遍历所有数据结构的统一方法。
当使用for…of循环遍历某种数据结构的时候,该循环会自动去寻找Iterator接口。也就是说一种数据结构只要部署了Iterator接口,就可以用for…of循环去执行遍历操作。
for…of的应用场景
1.Array
2.Map和Set
3.函数的arguments对象(类似数组的对象)
4.DOM NodeList对象(类似数组的对象)
5.字符串
6.Generator对象
下面依次介绍各种应用场景:
(1)数组
数组默认部署了Iterator接口:
let a=[1,2,3];
for(let item of a){
console.log(item);
}
//1
//2
//3
数组的遍历器接口只返回具有数字索引的属性
首先,我们来看一下什么是数组中具有数字索引的属性?
在javascript中,字符串是可以作为数组下标的:
let b=[];
b["q"]=1;
b["w"]=2;
console.log(b.length);//0
用字符串作为下标时,是不会影响到数组的length属性的,也就是说虽然通过字符串下标插入了数组元素,但数组的length属性的值是不会增加的。
接下来我们把数组b打印出来,可以看到length属性值为0:
首先,我们明确数组是对象的实例:
let b = [];
b["q"] = 1;
b["w"] = 2;
console.log(typeof b);//object
在数组中使用字符串下标的时候,就是为数组对象添加了一个属性,属性名称就是使用的字符串下标。通过字符串下标为数组对象添加属性不会改变数组的length属性值。
因为用字符串下标时实际上是为数组添加了属性,所以下面两种写法是等价的:
let b = [];
b.q = 1;
b.w = 2;
console.log(b.length);//0
//等价于
let b = [];
b["q"] = 1;
b["w"] = 2;
console.log(b.length);//0
现在我们就明白了非数字索引的属性也就是使用字符串下标为数组添加的属性,当然还有一种情况需要注意:数字和数字字符串
let b = [];
b["0"] = 0;
b["1"] = 1;
console.log(b.length);//2
console.log(b);//[0,1]
使用数字字符串作为数组的下标是会改变数组的length属性值的。
for…of循环调用遍历器接口,而遍历器接口只返回具有数字索引的属性:
let b = [0,1];
b["q"] = 3;
console.log(b.length);//2
for (let item of b) {
console.log(item);
}
//0
//1
(2)Set和Map结构
Set结构:
const set = new Set([1, 2, 3]);
for (let item of set) {
console.log(item);
}
//1
//2
//3
Map结构:
const map = new Map([
[1, "one"],
[2, "two"],
[3, "three"]
]);
for (let item of map) {
console.log(item);
}
// [1, "one"]
// [2, "two"]
// [3, "three"]
const map1 = new Map();
map1.set("1","one");
map1.set("2","two");
map1.set("3","three");
for (let item of map1) {
console.log(item);
}
// [1, "one"]
// [2, "two"]
// [3, "three"]
需要注意以下两点:
1.遍历的顺序就是各个成员被添加进数据结构的顺序。
2.Set结构返回的是一个值,而Map结构无论如何构造,遍历时返回的都是一个数组,这个数组的两个成员分别为当前Map成员的键名和键值。
(3)字符串
let a ="hello";
for (let item of a) {
console.log(item);
}
//h
//e
//l
//l
//0
对于字符串,for…of循环还可以正确识别32位UTF-16字符:
for (let x of 'a\uD83D\uDC0A') {
console.log(x);
}
// 'a'
// '\uD83D\uDC0A'
(4)类似数组的对象
类似数组的对象指的是具有数值键名和length属性的对象。
如NodeList对象、函数的arguments对象:
// DOM NodeList对象
let paras = document.querySelectorAll("p");
for (let p of paras) {
p.classList.add("test");
}
// arguments对象
function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs('a', 'b');
// 'a'
// 'b'
需要注意的是并不是所有的类似数组的对象都有Iterator接口,为了解决这个问题可以使用Array.from方法将其转为数组:
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
// 报错
for (let x of arrayLike) {
console.log(x);
}
// 正确
for (let x of Array.from(arrayLike)) {
console.log(x);
}
(5)对象
对于没有部署Iterator接口的普通对象,不能直接使用for…of循环遍历。
但是可以使用for…in遍历键名:
let simple = {
q: 1,
qw: 2,
};
for (let item in simple) {
console.log(item);
}
// q
// qw
for (let item of simple) {
console.log(item);
}
//Uncaught TypeError: simple is not iterable
如果想使用for…of遍历得到对象的键名和键值,可以有以下两种方法,核心思想都是先转化为数组,然后使用for…of遍历。
方法一:
for (var key of Object.keys(someObject)) {
console.log(key + ': ' + someObject[key]);
}
Object.keys() 方法会返回一个数组,这个数组是由一个给定对象的自身可枚举属性组成的。
方法二:
用Generator函数重新包装
let simple = {
q: 1,
qw: 2,
};
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(simple)) {
console.log(key, ':', value);
}
//q:1
//qw:2
(6)for…of与keys()、values()、entries()方法的结合使用
下面是一个例子:
let a = ["hello","world"];
for (let item of a.entries()) {
console.log(item);
}
//[0,"hello"]
//[1,"world"]
(7)其他的遍历语法
1.for循环
2.数组内置的forEach方法:
对于forEach方法除非使用try/catch,否则无法中途停止循环,break或者return都无法使循环中途停止。而for…of循环可以与break,continue和return配合使用,中途停止循环:
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}
上面的例子,会输出斐波纳契数列小于等于 1000 的项。如果当前项大于 1000,就会使用break语句跳出for…of循环。
3.for…in循环
for…in语句以任意顺序遍历一个对象的可枚举属性。
//数组
let a=["q","w","e"];
for (let item in a) {
console.log(item)
}
//0
//1
//2
//对象
let b={
q:1,
w:2,
e:3
};
for (let item in b) {
console.log(item)
}
//q
//w
//e
//字符串
let c = "hello";
for (let item in c) {
console.log(item)
}
//0
//1
//2
//3
//4
4.for…of循环与for…in循环的比较
a、数组的键名是数字,但是for…in循环是以字符串作为键名:
let a = ["q", "w", "e"];
for (let item in a) {
console.log(item+" "+typeof item)
}
//0 string
//1 string
//2 string
let b = ["q", "w", "e"];
for (let item of b.keys()) {
console.log(item+" "+typeof item)
}
//0 number
//1 number
//2 number
b、在数组遍历中,for…in循环会遍历非数字索引的属性,而for…of循环不会
let b = [0,1];
b["q"] = 3;
for (let item of b) {
console.log(item);
}
//0
//1
for (let item in b) {
console.log(item);
}
//0
//1
//q