一、前言
我们知道,在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
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就能完成我们的工作了,但是这些更多的细节问题你有注意到吗?好了,这篇博客就到这里了,希望能带给大家一些东西~
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/178532.html原文链接:https://javaforall.net
