js中遍历对象的几种方法

一、前言

我们知道,在js的对象中,有下面几种属性:

  • 可枚举属性
  • 不可枚举属性
  • 从原型链上继承的属性
  • 以Symbol作为key值

今天,我们就来谈一谈如何遍历这些属性。

二、遍历可枚举属性

  首先,遍历可枚举属性是非常常见的一个需求,我们平时比较常用的方法是for in和Object.keys(),这两个方法都能遍历可枚举属性,我们看看如下的代码:

let obj = {
  name: "klx",
  sex: "man",
  age: 21
}

Object.keys(obj).forEach((key, index) => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
//age:21
for(key in obj) {
  console.log(`${key}:${obj[key]}`);
}
//name:klx
//sex:man
//age:21

  可以看到,他们并没有什么区别。那么,这两个方法是完全一样的吗?我们再来看看下面的代码:

let obj = {
  name: "klx",
  sex: "man",
  age: 21
}

let parent = {
  parentName: "parent",
  parentSex: "man",
  parentAge:40
}

Object.setPrototypeOf(obj, parent);

Object.keys(obj).forEach((key, index) => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
//age:21
for(key in obj) {
  console.log(`${key}:${obj[key]}`);
}
//name:klx
//sex:man
//age:21
//parentName:parent
//parentSex:man
//parentAge:40

  这时我们可以看到,第二种方法比第一种多出来几个属性。所以,他们的区别就体现出来了,我们在上面的代码中将obj的原型对象设置为了另一个对象,而for in把原型链上的属性也遍历了出来,但是Object.keys没有。所以,他们两个的区别就是:for in会遍历原型链上的属性,Object.keys不会遍历原型链上的属性。所以,我们平时最好使用Object.keys去遍历对象,会避免一些不必要的麻烦。

二、遍历不可枚举属性

  什么是不可枚举属性?如果不知道这个问题的同学可以参考MDN的介绍,这里我就不详细解释了。我们先来看看下面的代码:

let obj = {
  name: "klx",
  sex: "man",
  age: 21
}

Object.defineProperty(obj, 'age', {
  enumerable:false,
  configurable:true,
  writable:true
})
Object.keys(obj).forEach((key, index) => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
for(key in obj) {
  console.log(`${key}:${obj[key]}`);
}
//name:klx
//sex:man

  在这里,我们把age改成了不可枚举属性(因为我们的年龄不能随便告诉别人啊~)。所以可以看见,不管我们用for in还是Object.keys都无法遍历到age属性。那么,我们这时该如何才能遍历到age这个属性呢?
  我们可以使用Object.getOwnPropertyNames()这个方法,来看下面的代码

let obj = {
  name: "klx",
  sex: "man",
  age: 21,
}
let a = Symbol('a');

obj[a] = 'Symbol';

Object.defineProperty(obj, 'age', {
  enumerable:false,
  configurable:true,
  writable:true
})

let parent = {
  parentName: "parent",
  parentSex: "man",
  parentAge:40
}

Object.setPrototypeOf(obj, parent);

Object.keys(obj).forEach((key, index) => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
for(key in obj) {
  console.log(`${key}:${obj[key]}`);
}
//name:klx
//sex:man
//parentName:parent
//parentSex:man
//parentAge:40
Object.getOwnPropertyNames(obj).forEach(key => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
//age:21

  所以,使用Object.getOwnPropertyNames就能遍历到这个对象除掉以Symbol作为名称的属性,同时也不能遍历到原型链上的属性。

三、遍历以Symbol作为key值的属性

  那么,如果我们想要遍历以Symbol作为key值的属性该怎么办呢?这时我们可以用这个方法:Object.getOwnPropertySymbols。

let obj = {
  name: "klx",
  sex: "man",
  age: 21,
}
let a = Symbol('a');

obj[a] = 'Symbol';

Object.defineProperty(obj, 'age', {
  enumerable:false,
  configurable:true,
  writable:true
})

let parent = {
  parentName: "parent",
  parentSex: "man",
  parentAge:40
}

Object.setPrototypeOf(obj, parent);

Object.keys(obj).forEach((key, index) => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
for(key in obj) {
  console.log(`${key}:${obj[key]}`);
}
//name:klx
//sex:man
//parentName:parent
//parentSex:man
//parentAge:40
Object.getOwnPropertyNames(obj).forEach(key => {
  console.log(`${key}:${obj[key]}`);
})
//name:klx
//sex:man
//age:21
Object.getOwnPropertySymbols(obj).forEach(key => {
  console.log(obj[key]);
})
//Symbol

  那么问题来了,如果我想遍历一个对象上的全部属性,又该怎么做呢?这时我们可以用Reflect.ownKeys()方法来达到我们的目的,他会放回该对象上的所有属性,但是不包括原型链上的属性:

let obj = {
  name: "klx",
  sex: "man",
  age: 21,
}
let a = Symbol('a');

obj[a] = 'Symbol';

Object.defineProperty(obj, 'age', {
  enumerable:false,
  configurable:true,
  writable:true
})

let parent = {
  parentName: "parent",
  parentSex: "man",
  parentAge:40
}

Object.setPrototypeOf(obj, parent);

console.log(Reflect.ownKeys(obj));
//[ 'name', 'sex', 'age', Symbol(a) ]

四、遍历自身以及原型链上的所有属性

  那么,如果我不仅仅想遍历自身所有属性,还想遍历原型链上的所有属性,该怎么办?这时,就没有现成的api供我们调用了,但是我们可以自己写一个方法来遍历:

let obj = {
  name: "klx",
  sex: "man",
  age: 21,
}
let a = Symbol('a');

obj[a] = 'Symbol';

Object.defineProperty(obj, 'age', {
  enumerable:false,
  configurable:true,
  writable:true
})

let parent = {
  parentName: "parent",
  parentSex: "man",
  parentAge:40
}

Object.setPrototypeOf(obj, parent);

function traverse(obj, arr) {
  arr.push(...Reflect.ownKeys(obj));
  if(Object.getPrototypeOf(obj)) {
    traverse(Object.getPrototypeOf(obj), arr);
  }
  return arr;
}

console.log(traverse(obj, []));
/*
[ 'name',
  'sex',
  'age',
  Symbol(a),
  'parentName',
  'parentSex',
  'parentAge',
  'constructor',
  '__defineGetter__',
  '__defineSetter__',
  'hasOwnProperty',
  '__lookupGetter__',
  '__lookupSetter__',
  'isPrototypeOf',
  'propertyIsEnumerable',
  'toString',
  'valueOf',
  '__proto__',
  'toLocaleString' ]
*/

  这样,我们就把该对象以及原型链上的属性全部遍历出来了,当然,你也可以根据自己的需求,利用上述几个方法组合,来遍历自己想要的属性。

五、结语

  不要小看一个简单的问题,其中可能有许多你不知道的小知识。我们平时简单遍历一个对象可能单纯使用for in就能完成我们的工作了,但是这些更多的细节问题你有注意到吗?好了,这篇博客就到这里了,希望能带给大家一些东西~

发布了24 篇原创文章 · 获赞 46 · 访问量 5146

猜你喜欢

转载自blog.csdn.net/qq_38164763/article/details/102764133