Problem arises in such recursive function when you have circular reference. You have to keep track of what objects you already parsed.
Let's say we have this object :
var test = {
_handlers: {
length: 1
},
child1: {
member1: {
_handlers: [7, 9, 12],
child: {
morehandlers: {
_handlers: {
length: 7
}
},
_handlers: [1]
}
},
member2: {
_handlers: {
length: 1
}
}
},
child2: {
value: 2
},
child3: {
last: {
_handlers: {
length: 7
}
}
}
}
Total handlers count should be 20.
And then we add a circular reference :
test.child1.member3 = test;
Here is how I would handle it without thinking of performances :
let parsedHandlers = null;
let handlersCountLaunched = false;
function countHandlers(obj) { // Cannot be async
let countObj = obj;
let count = 0;
for (let i = 0; i < parsedHandlers.length; i++) {
if (countObj === parsedHandlers[i]) {
countObj = null;
break;
}
}
if (countObj !== null) {
parsedHandlers.push(countObj);
for (let k in obj) {
if (k === "_handlers") {
count += obj[k].length;
} else if (typeof obj[k] === 'object') {
count += this.countHandlers(obj[k]);
}
}
}
return count;
}
function getHandlersCount(mainObj) {
if (!handlersCountLaunched) {
parsedHandlers = [];
handlersCountLaunched = true;
let count = countHandlers(mainObj);
handlersCountLaunched = false;
parsedHandlers = null;
return count;
} else {
console.error('TimingError : getHandlersCount() has been called and has not yet finished counting');
return -1;
}
}
console.log(getHandlersCount(test));
In javascript, unless you have setup a mapping logic, you can't retrive the parent object of a member. With a circular reference in the object, you will probably end up with the total amount of handlers in the object tree, unless you select a branch with no circular reference.
Setto remember the ones you've already seen.