Search code examples
javascriptecmascript-6prototype

ES6 access this in class inheritance with prototype


I am trying to break large nodejs class files into smaller ones by using prototyping. It works, as long as I do not access this variables in a super-class.

This code has two classes: ClassB extends ClassA. In the constructor of ClassB we pass a variable that ClassA, the super-class, set to this.page. We then try to access this.page from a prototype functionA.

class ClassA  {
    constructor(page) {
        this.page = page
        console.log('[classA/constructor] has page', this.page)

    }

    /*
     *  Example A: This works!
     */
    // functionA = () => {
    //     console.log("has page", this.page)
    // }

    /*
     *  Example B: This works!
     */
    // functionA = function() {
    //     console.log("has page", this.page)
    // }

    /*
     *  Example C: This does not work!
     */
    // functionA() {
    //     console.log("has page", this.page)
    // }
}

/*
* Example D: This is what I really want to get working... does not work
*/
ClassA.prototype.functionA = function functionA() {
    console.log('[classA/functionA] has page', this.page)
}

class ClassB extends ClassA {
    constructor(page) {
        console.log('[classB/constructor] has page', page)
        super(page)
    }
}

ClassB.prototype.functionA = function () {
    console.log('[classB/functionA] has page', this.page)
    this.__proto__.functionA()
}

const classB = new ClassB('YES!');
classB.functionA();

Uncommenting and running Example A and example B works fine and gives the following, correct, output:

[classB/constructor] has page YES!
[classA/constructor] has page YES!
has page YES!

When switching to prototype (Example D, the uncommented one in the code) it break, output is:

[classB/constructor] has page YES!
[classA/constructor] has page YES!
[classB/functionA] has page YES!
[classB/functionA] has page undefined
[classA/functionA] has page undefined

I figure it has to do with binding, as Example C does not work either. But I can not understand why..


Solution

  • Calling

    this.__proto__.functionA()
    

    is the problem. You're calling functionA() in a way that will not have the right object in this inside of functionA(). Calling it that way will have this.__proto__ as the value of this inside of functionA(). That is clearly not what you want.

    I don't know why you're using this.__proto__.functionA() in the first place rather than this.functionA(), but you could force the right this with:

    this.__proto__.functionA().call(this)
    

    In case you didn't realize, when you call obj.method(), Javsacript sets the value of this inside of method() to obj. So, when you do this.__proto__.functionA(), you're telling JS to set the value of this inside of functionA() to this.__proto__ which is not what you want.


    If what you're trying to do is to call classA's version of functionA from within classB's functionA override, then you would do that like this:

    ClassB.prototype.functionA = function () {
        console.log('[classB/functionA] has page', this.page)
        ClassA.prototype.functionA.call(this);
    }
    

    And, if you use ES6 class syntax for defining your methods (instead of prototype manipulation), you would just use:

    super.functionA();
    

    to call the base class version of that function.