遍历对象的几种方法

遍历对象的几种方法

JavaScript 当中遍历对象方式有许多种,比如常见的 for-inObject.keys(obj) 等等,但是对于它们之间的区别一直有些模糊,所以今天就抽些时间将 JavaScript 当中涉及到遍历对象的 API 整体的梳理了一遍,看看它们的使用方式和之间的区别,先来小小的总结一下,大致有以下几种

方法 return prototype Symbol 如果参数不是对象
Object.keys(obj) 返回所有可枚举属性 不包括 不含 ES6 之前报错,之后强制转换为对象
for-in 返回所有可枚举属性 包括 不含 ES6 之前报错,之后强制转换为对象
Object.getOwnPropertyNames(obj) 返回所有自身属性,也包括不可枚举属性,如果是数组,那么 length 属性也包含其中 不包括 不含 ES6 之前报错,之后强制转换为对象
Reflect.ownKeys(obj) 返回所有自身属性,不管是否可枚举,不管是不是 Symbol,一律返回 包括 包含 报错
for-of 返回当前对象上的每一个属性(迭代) 不包括 不含 普通的对象不能直接使用 for-of,否则会报错,必须部署了 iterator 接口才能使用,可以考虑使用 Object.entries(obj)

通过上表可以发现,Reflect.ownKeys(obj) 这个方法很强大,因为它可以返回所有自身属性,不管是否可枚举,不管是不是 Symbol,一律返回,下面我们就一个一个来看看它们之间的区别

Object.keys(obj)

Object.keys(obj) 方法返回一个表示给定对象的所有可枚举属性的字符串数组,注意这里是可枚举属性,它包括对象自身的所有可枚举属性,但是不包含继承而来的属性,也不包含 Symbol 属性,使用如下

1
2
3
4
5
6
7
var arr = ['a', 'b', 'c']
console.log(Object.keys(arr)) // ['0', '1', '2']

// -------------------------------

var an_obj = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.keys(an_obj)) // ['2', '7', '100']

它的参数如果不是一个对象,那么在 ES6 之前报错,之后会强制转换为对象,返回的是一个表示给定对象的所有可枚举属性的字符串数组(但不包括原型中的属性),这里有一个需要注意的地方,即使用 Object.keys(obj) 循环遍历对象时返回的顺序不一定正确,至于为什么会这样,可以参考 为什么 Object.keys 的返回值会自动排序 这篇文章

for-in

for-in 语句以任意顺序遍历一个对象的可枚举属性,对于每个不同的属性,语句都会被执行

1
2
3
4
5
6
var c = Symbol('c')
var obj = { a: 1, b: 2, [c]: 3 }

for (var prop in obj) {
console.log(prop) // a b
}

它将返回所有可枚举属性,包括原型中的属性,但是不包含 Symbol 属性,如果参数不是一个对象,在 ES6 之前报错,之后会强制转换为对象,for-in 循环只遍历『可枚举』属性,循环将迭代对象的所有可枚举属性和从它的构造函数的 prototype 继承而来的(包括被覆盖的内建属性,即包括原型中的属性),但是因为其返回值的顺序问题,它不应该被用来迭代一个下标顺序很重要的 Array,同 Object.keys(obj) 一样,for-in 并不能够保证返回的是按一定顺序的索引,如果仅迭代自身的属性,而不是它的原型,可以使用

  • getOwnPropertyNames()
    • 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组
  • hasOwnProperty()
    • 来确定某属性是否是对象本身的属性(一般使用这个来过滤)
  • propertyIsEnumerable()
    • 返回一个布尔值,表明指定的属性名是否是当前对象可枚举的自身属性

Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名,『包括』不可枚举属性组成的数组,但不会获取原型链上的属性,如果参数不是一个对象,在 ES6 之前报错,之后会强制转换为对象

1
2
3
var arr = ['a', 'b', 'c']

console.log(Object.getOwnPropertyNames(arr).sort()) // ['0', '1', '2', 'length']

它会返回所有的自身属性,不包括原型中属性,不含 Symbol 属性,但是它与前两种方式不同的是,它会包括不可枚举的属性 length,它枚举属性的顺序与通过 for-in 循环(或 Object.keys())迭代该对象属性时是一致的,如果只获取到可枚举属性,使用 Object.keys() 或用 for-in 循环(配合 hasOwnProperty()

Reflect.ownKeys

静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组,这个方法在之前也提到过了,十分强大,它会返回一个数组,里面包含了对象的所有自身属性,不管是否可枚举,不管是不是 Symbol,一律返回,参数不是一个对象的话,那么会抛出一个错误

1
2
3
Reflect.ownKeys({ z: 3, y: 2, x: 1 })   // [ 'z', 'y', 'x' ]

Reflect.ownKeys([]) // ['length']

它的返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

for-of

for-of 语句在可迭代对象(包括 ArrayMapSetstringTypedArrayarguments 对象等)上创建一个迭代循环,对每个不同属性的属性值,调用一个自定义的有执行语句的迭代挂钩

所谓的 TypedArray,它描述的是一个底层的二进制数据缓存区的一个类似数组(array-like)视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let iterable = [10, 20, 30]

for (let value of iterable) {
console.log(value)
}
// 10
// 20
// 30

//-----------------------------

let iterable = 'foo'

for (let value of iterable) {
console.log(value)
}
// 'f'
// 'o'
// 'o'

for-of 会返回当前对象上的每一个属性,遍历的是『当前对象』上的每一个属性值,不包括原型上的,但是并不适用于所有的 Object,只能迭代出拥有 iterator 的对象,通过观察上面的例子我们可以发现,我们在其中分别使用的是数组跟字符串,这两者都是默认部署了 Iterator 接口,所以可以使用 for-of 来进行遍历,那么对于普通的对象会是什么情况呢,我们来看下面这个例子

1
2
3
4
5
6
var c = Symbol('c')
var obj = { a: 1, b: 2, [c]: 3 }

for (let [key, value] of obj) {
console.log(`${key}: ${value}`) // TypeError: obj is not iterable
}

运行以后可以发现,对于普通的对象,for-of 不能直接使用,否则会报错,在这种情况下,我们可以使用 Object.entries() 来将我们的对象包裹一下即可(当然,你也可以手动的为对象添加 Iterator 接口),该方法返回一个给定对象『自身可枚举』属性的键值对数组,其排列与使用 for-in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性),因为返回的是数组,所以具有 Iterator 接口可以直接被 for-of 遍历

1
2
3
4
5
6
for (let [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`)
}

// a: 1
// b: 2

Object.getOwnPropertySymbols()

最后我们再来看一个比较特殊的方法,因为在上面列举的一些 API 当中我们可以发现,其中有很多方法的输出结果都是不包括 Symbol 属性的,所以 JavaScript 提供了一个 Object.getOwnPropertySymbols() 方法,专门用来返回一个给定对象自身的所有 Symbol 属性的数组

但是有一点需要注意,所有的对象在初始化的时候不会包含任何的 Symbol,除非你在对象上赋值了 Symbol,否则 Object.getOwnPropertySymbols() 只会返回一个空的数组,我们还是使用 Reflect.ownKeys(target) 当中的例子来进行对比说明,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let a = Symbol.for('a')
let b = Symbol.for('b')

let obj = {
[a]: 10,
[b]: 20,
key1: 30,
key2: 40
}

Object.getOwnPropertyNames(obj) // [ 'key1', 'key2' ]

Object.getOwnPropertySymbols(obj) // [ Symbol(a), Symbol(b) ]

Reflect.ownKeys(obj) // [ 'key1', 'key2', Symbol(a), Symbol(b) ]

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×