写在最前面

本文主要是对JS对象(不含数组、Map、Set结构)的 7个遍历方法进行总结归纳。

定义对象

首先让我们定义如以下代码所示的对象player:

  1. const player = {
  2. name: 'Allen Iverson',
  3. [Symbol('birthday')]: '1975/06/07',
  4. };
  5. Object.defineProperties(player, {
  6. isHallofFame: {
  7. enumerable: false,
  8. value: true,
  9. },
  10. [Symbol('ScoreKingTime')]: {
  11. enumerable:false,
  12. value: 4,
  13. },
  14. });
  15. Object.defineProperties(player.__proto__, {
  16. university: {
  17. enumerable: true,
  18. value: 'Georgetown',
  19. },
  20. team: {
  21. enumerable: false,
  22. value: '76ers',
  23. },
  24. [Symbol('country')]: {
  25. enumerable: true,
  26. value: 'USA',
  27. },
  28. [Symbol('hometown')]: {
  29. enumerable: false,
  30. value: 'Virginia',
  31. },
  32. });

如上述代码所示,定义了一个player的对象,其共有以下8个属性:

原型属性 可枚举 Symbol属性
name Allen Iverson
birthday 1975/06/07
isHallofFame true
ScoreKingTime 4
university Georgetown
team 76ers
country USA
hometown Virginia

通过控制台的输出观察也更直观:
answer.png
其中浅颜色的属性都是不可枚举的属性,proto下的属性则为其原型上(即Object.prototype)的属性,Symbol类型的值自然为Symbol属性

for…in

MDN:The for…in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties.

  1. for(const name in player) {
  2. console.log('name:', name);
  3. }
  4. // name: name
  5. // name: university

for…in 循环是我们较为常用的遍历对象方法了,结合MDN里所言以及输出结果不难得出其遍历的属性,包含自身以及原型上所有可枚举的属性,不包含Symbol属性。因为其可遍历到原型上可枚举的属性,因此我们的代码中通常会多出一句这样的判断(如果我们不需要原型上的属性):

  1. for(const name in player) {
  2. if (Object.prototype.hasOwnProperty.call(player, name)) {
  3. console.log('name:', name);
  4. }
  5. }
  6. // name: name

Object.keys

MDN:The Object.keys() method returns an array of a given object’s own enumerable property names, iterated in the same order that a normal loop would.

  1. const keys = Object.keys(player);
  2. console.log('keys:', keys);
  3. // keys: ["name"]

Object.keys大概是我们最常用的遍历方法了,如在Vue源码对data进行遍历响应式处理也是用这个方法。通过输出结果也表明:其返回的是所有自身可枚举的属性(不含Symbol属性),不包含原型上的任何属性。

Object.getOwnPropertyNames

MDN:The Object.getOwnPropertyNames() method returns an array of all properties (including non-enumerable properties except for those which use Symbol) found directly in a given object.

  1. const ownPropertyNames = Object.getOwnPropertyNames(player);
  2. console.log('ownPropertyNames:', ownPropertyNames);
  3. // ownPropertyNames: ["name", "isHallofFame"]

Object.getOwnPropertyNames返回的是所有自身的属性(包含不可枚举属性但不包含Symbol属性),不包含原型上的任何属性。

Object.getOwnPropertySymbols

MDN:The Object.getOwnPropertySymbols() method returns an array of all symbol properties found directly upon a given object.

  1. const ownPropertySymbols = Object.getOwnPropertySymbols(player);
  2. console.log('ownPropertySymbols:', ownPropertySymbols);
  3. // ownPropertySymbols: [Symbol(birthday), Symbol(ScoreKingTime)]

通过输出不难得出Object.getOwnPropertySymbols返回的是自身的所有Symbol属性(包含不可枚举的),但是不含原型上的任何属性。

Reflect.ownKeys

MDN:The static Reflect.ownKeys() method returns an array of the target object’s own property keys.

  1. const ownKeys = Reflect.ownKeys(player);
  2. console.log('ownKeys:', ownKeys)
  3. // ownKeys: ["name", "isHallofFame", Symbol(birthday), Symbol(ScoreKingTime)]

Reflect.ownKeys返回的是自身的所有属性(包含不可枚举的以及所有Symbol属性),不包含原型 上的任何属性。

Object.values

MDN:The Object.values() method returns an array of a given object’s own enumerable property values, in the same order as that provided by a for…in loop. (The only difference is that a for…in loop enumerates properties in the prototype chain as well.)

  1. const values = Object.values(player);
  2. console.log('values:', values);
  3. // values: ["Allen Iverson"]

Object.values同Object.keys一样,其遍历的是所有自身可枚举的属性(不含Symbol属性),不包含原型上的任何属性。但与Object.keys不同的是:其返回的不再是key值而是value值集合。

Object.entries

MDN: The Object.entries() method returns an array of a given object’s own enumerable string-keyed property [key, value] pairs, in the same order as that provided by a for…in loop. (The only important difference is that a for…in loop enumerates properties in the prototype chain as well).

  1. const entries =Object.entries(player);
  2. console.log('entries:', entries);
  3. // entries: [["name", "Allen Iverson"]]

其也同Object.keys一样,遍历的是所有自身可枚举的属性(不含Symbol属性),不包含原型上的任何属性。不同的是,其返回值是 [key, value]的集合。Object.entries在我们平时的开发中可能很少用到,但是其可配合Object.fromEntries一起使用:有以下代码:

  1. // 对象转换
  2. const object1 = { a: 1, b: 2, c: 3 };
  3. const object2 = Object.fromEntries(
  4. Object.entries(object1)
  5. .map(([ key, val ]) => [ key, val * 2 ])
  6. );
  7. console.log(object2);
  8. // { a: 2, b: 4, c: 6 }

总结

结合文章中的代码输出结果可得到以下表格:

含原型属性 含不可枚举 含Symbol属性 返回值
for…in key
Object.keys [key…]
Object.getOwnPropertyNames [key…]
Object.getOwnPropertySymbols 是(只有symbol) [key…]
Reflect.ownKeys [key…]
Object.values [value…]
Object.entries [[key,value]…]

简而言之:

  • 只有for…in可以遍历到原型上的属性
  • Object.getOwnPropertyNames和Reflect.ownKeys可获取到不可枚举的属性
  • Object.getOwnPropertySymbols和Reflect.ownKeys可获取到Symbol属性