Recursively unpacking a javascript object

I'm seeing a React course, we were studying fundamentals of javascript for a while. We saw how to access the content of an object and display it in console. My solution for the problem of displaying an unknown object was the following:

function unpackObject(obj, tabs = '') {
const props = Object.getOwnPropertyNames(obj);

let v;
props.forEach(p => {
v = obj[p];
//v = Object.getOwnPropertyDescriptor(obj, p).value; | side question: is it better?

if (v instanceof Object) {
console.log(${tabs}${p} ${Object.prototype.toString.call(v)}); if (v instanceof Array) v.forEach((x, i) => console.log(${tabs}\t[${i}]:${x}));
else
unpackObject(v, ${tabs}\t); } else console.log(${tabs}${p}:${v});
});
}

// Testing code:
const testObject = {
name: 'Miguel',
surname: 'Avila',
age: undefined, //LOL
marital_status: 'Single',
hobbies: ['Write Code', 'Watch YT Videos', 'etc. idk'],
contact: {
phones: ['xxxxxxxxxx', 'xxxxxxxxxx'],
}
};

unpackObject(testObject);

My questions are: Can this code be faster and/or shorter? Are there any tricks capable of improving it for massive objects? (I mean, I fear recursion because when it goes wrong it's a big deal).

• Most browsers already pretty-log nested objects in standard formats with collapseable tabs for large structures. There's also console.log(JSON.stringify(obj, null, 2)). Builtins can be assumed to be optimized already, by large communities of smart people. What is the point of this code? If you're just using it for logging, it seems premature to try to optimize it from a performance standpoint. Is it the application's bottleneck that's causing problems for customers? Sep 18, 2021 at 18:24
• @ggorlen I'm asking if this code is good or can be improved, I didn't know about stringify which is sincerely good to know, thanks; However my concern was the code as such, if there's something to improve to make it faster or shorter. (Basically it's an exercise) Sep 19, 2021 at 2:34

Try this testObject. You may want to do something to test for recursion, e.g. an array or map that tracks objects that you have already seen.

function unpackObject(obj, tabs = '') {
const props = Object.getOwnPropertyNames(obj);

let v;
props.forEach(p => {
v = obj[p];
//v = Object.getOwnPropertyDescriptor(obj, p).value; | side question: is it better?

if (v instanceof Object) {
console.log(${tabs}${p} ${Object.prototype.toString.call(v)}); if (v instanceof Array) v.forEach((x, i) => console.log(${tabs}\t[${i}]:${x}));
else
unpackObject(v, ${tabs}\t); } else console.log(${tabs}${p}:${v});
});
}

// Testing code:
let testObject = {
name: 'Miguel',
surname: 'Avila',
age: undefined, //LOL
marital_status: 'Single',
hobbies: ['Write Code', 'Watch YT Videos', 'etc. idk'],
contact: {
phones: ['xxxxxxxxxx', 'xxxxxxxxxx'],