js中遍历的方法有很多,例如普通的for(;;),for in,while,forEach,some,map….等等.
本文主要介绍js中的for in循环和for of循环
for in#
for in的常见用法主要是用来遍历一个对象.例如
1
2
3
4
5
|
var obj = {a:1,b:2,c:3};
for(var k in obj){
console.log(k)
}
//a b c
|
上述代码中for in通过遍历对象的键
来实现输出对应value.也就是说for in 其实是遍历对象键
我们来试试遍历数组
1
2
3
4
5
|
var arr = [1,2,3];
for(var k in arr){
console.log(k)
}
//0 1 2
|
其实这里和for(;;)循环挺像的.我们因为数组的索引在这里被看成了键
.
我们还是可以通过这个键去获取值例如这样
1
2
3
4
5
|
var arr = [1,2,3];
for(var k in arr){
console.log(arr[k])
}
//1 2 3
|
其实熟悉for in的同学都知道.数组是不能用for in来遍历的.因为不安全.
有些时候我们可能需要给Array对象拓展一些函数或者属性,例如:
1
2
3
4
5
6
7
8
|
Array.prototype.foo = function(){
//给数组原型上添加一个自定义函数
};
var arr = [1,2,3];
for(var i in arr){
console.log(i)
}
//0 1 2 foo
|
可以看到for in直接将我们拓展的函数一并给遍历出来了.看下MDN上的解释
从图中我们看到一个名词可枚举属性
.那么现在就可以介绍我们的for of
在介绍for of 之前我们先来了解一下这个名词到底是什么
可枚举属性#
其实说得直白点.可枚举也就是说可以遍历出来
“可以出现在对象属性的遍历中” (你不知道的JavaScript)
这里呢,引出一个Object的函数Object.defineProperty()
:
1
2
3
4
5
6
7
8
9
10
11
|
var obj = {a:1,b:2};
Object.defineProperty(obj,'c',{
enumerable:false,
value:3
})
obj.c //3
obj.hasOwnProperty('c'); //true;
for(var k in obj){
console.log(k)
}
//a b
|
上述代码我们只关心一个属性enumerable
.如果将它赋值为false,那么这个属性将不可被枚举.也自然不可被遍历
我们改一下之前用for in遍历数组的例子
1
2
3
4
5
6
7
8
9
10
11
|
Object.defineProperty(Array,'foo',{
enumerable:false,
value:function(){
//给数组原型上添加一个自定义函数
}
});
var arr = [1,2,3];
for(var i in arr){
console.log(i)
}
//0 1 2
|
可以看到我们可以通过Object.defineProperty()将enumerable
设置成false,新增的foo就不会被遍历出来了.
那么接下来就是es6中的for of方法了
for of#
for of遍历的只是数组内的元素,而不包括数组的原型属性method
和索引name
;
还是之前for in遍历数组的例子.我们改写成for of遍历
1
2
3
4
5
6
7
8
|
Array.prototype.foo = function(){
//给数组原型上添加一个自定义函数
};
var arr = [1,2,3];
for(var i of arr){
console.log(i)
}
//1 2 3
|
可以看到,尽管我们给Array拓展了一个foo函数,且没有设置其enumerable
为false.也依然可以拿到对应的值.
::: tip 提示
for of
是遍历元素,也就是value
.而不是key
:::
那,for of 的内部实现到底是怎样的呢.这里延伸一个新知识点 iterator
iterator#
for of循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有返回值 (你不知道的JavaScript)
上面之所以可以用for of遍历数组是因为数组有内置的iterator
.在用for of遍历的时候回自动调用next()返回.
那么我们手动来手动遍历一下:
1
2
3
4
5
6
|
var arr = [1,2,3];
var it = arr[Symbol.iterator]();
it.next();//{value:1,done:false}
it.next();//{value:2,done:false}
it.next();//{value:3,done:false}
it.next();//{value:undefined,done:true}
|
不难看出.我们通过调用next()
函数获取到了相应的value.直到返回对象中done
为true
;
那for of可以用来遍历对象吗?
1
2
3
4
5
|
var obj = {a:1,b:2,c:3};
for(var v of obj){
console.log(v);
}
//Uncaught TypeError: obj is not iterable
|
咦,报错了.回顾一下上面那句话for of循环首先会向被访问对象请求一个迭代器对象
,上述例子中obj并没有这个iterable
对象.所以不可用for of遍历
那就不能用for of 遍历对象了吗?
其实不然.我们只要给对象实现一个iterable
即可. 看下面例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
var myObject = {
a: 1,
b: 2,
c: 3
};
Object.defineProperty(myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function () {
var o = this;
var keys = Object.keys(o); //得到对象中所有key
var i = 0;//迭代计数器
return {
next: function () {
return {
value: o[keys[i++]],
done: i > keys.length
}
}
}
}
});
var it = myObject[Symbol.iterator]();
it.next();//{value: 1, done: false}
it.next();//{value: 2, done: false}
it.next();//{value: 3, done: false}
for (var v of myObject) {
console.log(v)
}
//1 2 3
|
以上就是对for in
for of
的简单介绍以及延伸出的可枚举属性
和iterator
的介绍