for in 和 for of 区别

for..in

for..in 循环可以用来遍历对象的可枚举属性列表(包括 [[Prototype]] 链

遍历数组下标时采用的是数字顺序(for 循环或者其他迭代器),但是遍历对象属性时的顺序是不确定的,在不同的 JavaScript 引擎中可能不一样。因此,在不同的环境中需要保证一致性时,一定不要相信任何观察到的顺序,它们是不可靠的。

for..of

for..of 循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的 next() 方法来遍历所有返回值。 数组有内置的 @@iterator,因此 for..of 可以直接应用在数组上。我们使用内置的 @@ iterator 来手动遍历数组,看看它是怎么工作的:

1
2
3
4
5
6
var myArray = [ 1, 2, 3 ];
var it = myArray[Symbol.iterator]();
it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { done:true }

我们使用 ES6 中的符号 Symbol.iterator 来获取对象的 @@iterator 内部属性。引用类似 iterator 的特殊属性时要使用符号名,而不是符号包含的值。此外,虽然看起来很像一个对象,但是 @@iterator 本身并不是一个迭代器对象,而是一个返回迭代器对象的函数——这点非常精妙并且非常重要。

和数组不同,普通的对象没有内置的 @@iterator,所以无法自动完成 for..of 遍历。

当然,你可以给任何想遍历的对象定义 @@iterator,举例来说:

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
var myObject = { a: 2, b: 3 };
Object.defineProperty( myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function() {
var o = this;
var idx = 0;
var ks = Object.keys( o );
return {
next: function() {
return {
value: o[ks[idx++]],
done: (idx > ks.length)
};
}; }
}
);
// 手动遍历 myObject
var it = myObject[Symbol.iterator](); it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { value:undefined, done:true }
// 用 for..of 遍历 myObject
for (var v of myObject) {
console.log( v );
}
// 2
// 3

Object.keys

Object.keys(..) 会返回一个数组,包含所有可枚举属性。
如果想获取一个对象的所有属性,甚至包括不可枚举的,可以使用 Object.getOwnPropertyNames(..)

for in 和 hasOwnProperty(..) 的区别在于是否查找 [[Prototype]] 链,Object.keys( )不会走原型链,而for in 会走原型链。

一般我们通过如下方式来获取对象自身的属性:

1
2
3
4
5
for (const key in object) {
if (object.hasOwnProperty(key)) {
const element = object[key];
}
}