Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript recursion within a class

I am trying to get a recursion method to work in a class context. Within my class I have the following method:

    countChildren(n, levelWidth, level) {
    if (n.children && n.children.length > 0) {
        if (levelWidth.length <= level + 1) {
            levelWidth.push(0);
        }
        levelWidth[level + 1] += n.children.length;
        n.children.forEach(function (n) {
            this.countChildren(n, levelWidth, level+1);
        });    
    }
    // Return largest openend width
    return levelWidth;
}

However, when I use this method (which worked before when I just used it as function countChildren = ...) it can't... find (?) itself: Cannot read property 'countChildren' of undefined at the recursion.

Does anyone have any ideas?

like image 575
David Montgomery Avatar asked Jul 17 '17 15:07

David Montgomery


People also ask

Can you do recursion in JavaScript?

However, while JavaScript's functional coding style does support recursive functions, we need to be aware that most JavaScript compilers are not currently optimized to support them safely. Recursion is best applied when you need to call the same function repeatedly with different parameters from within a loop.

Can a JavaScript function call itself?

A function can refer to and call itself.

Is recursion more efficient than iteration?

Iteration can be used to repeatedly execute a set of statements without the overhead of function calls and without using stack memory. Iteration is faster and more efficient than recursion. It's easier to optimize iterative codes, and they generally have polynomial time complexity.

How do I use recursion in TypeScript?

Basically there are two ways to code recursive functions in TypeScript: First by directly calling the function from within itself and. Second by using an indirect call.


2 Answers

The problem arises because within your loop, this gets redefined to the inner function scope.

countChildren(n, levelWidth, level) {
    var self = this; // Get a reference to your object.

    if (n.children && n.children.length > 0) {
        if (levelWidth.length <= level + 1) {
            levelWidth.push(0);
        }
        levelWidth[level + 1] += n.children.length;

        n.children.forEach(function (n) {
            // Use "self" instead of "this" to avoid the change in scope.
            self.countChildren(n, levelWidth, level+1);
        });    
    }
    // Return largest openend width
    return levelWidth;
}
like image 103
krillgar Avatar answered Sep 17 '22 19:09

krillgar


Try binding the method in the constructor.
Also, by using an arrow function for your forEach, you keep the scope of the class' this.

export class MyClass {
    constructor(){
        this.countChildren = this.countChildren.bind(this);
    }

    countChildren(n, levelWidth, level){ ... }


    countChildren(n, levelWidth, level) {
        if (n.children && n.children.length > 0) {
            if (levelWidth.length <= level + 1) {
                levelWidth.push(0);
            }
            levelWidth[level + 1] += n.children.length;
            n.children.forEach( n => { // arrow function do not need to rebind this
                this.countChildren(n, levelWidth, level+1);
            });    
        }
        // Return largest openend width
        return levelWidth;
    }
}
like image 38
Sumi Straessle Avatar answered Sep 18 '22 19:09

Sumi Straessle