19

I have 3 classes:

class A{
    DoStuff(){
        return "Called from A";
    }
}

class B extends A {

    constructor(){
        super();
        var baseDoStuff = super.DoStuff;
        this.DoStuff = function(){
            return baseDoStuff() + " and Called from B";
        }
    }
}

class C extends B { 
    constructor(){
        super();
        var baseDoStufffff = super.DoStuff;
        this.DoStuff = function(){
            return baseDoStufffff() + " and Called from C";
        }
    }
}

I expected class C's DoStuff() to call B's DoStuff() (which in turn would call A's).

However calling DoStuff() on C only returns "Called from A and Called from C". What am I doing wrong here? Shouldn't this call B's method too?

A working example of this can be found here:

Example

0

2 Answers 2

25

Whenever you need to use super Use class methods instead of class members:

class A{
    DoStuff(){
        return "Called from A";
    }
}

class B extends A {

    constructor(){
        super();      

    }   
    DoStuff (){
        return super.DoStuff() + " and Called from B";
    }
}

class C extends B { 
    constructor(){
        super();                
    }

    DoStuff(){
            return super.DoStuff() + " and Called from C";
    }
}

var c = new C();
console.log(c.DoStuff());

Try it (prints Called from A and Called from B and Called from C)

This is because super translates to .prototype i.e. super.DoStuff() becomes _super.prototype.DoStuff() and the only things available on .prototype are class methods.

More: http://basarat.github.io/TypeScriptDeepDive/#/super

Sign up to request clarification or add additional context in comments.

Comments

8

JavaScript doesn't really have methods. TypeScript tries to hide the workings of the prototype chain with familiar notation but like all abstractions it's imperfect. The gist of the prototype chain is that when you call c.DoStuff() it looks for DoStuff on the c instance, than the prototype (C), then its prototype (B), then its prototype (A), and then its prototype (Object).

That applies not just to functions but to any member you might look up. Functions aren't very special in JavaScript, which ironically is what makes them powerful.

Using TypeScript's syntax in your A class you're putting a function DoStuff in A's prototype. Every instance of A you call DoStuff on will look for it in the instance (and probably failing to find it there) look at the prototype and see that function you defined. So far so good.

Then you defined a class B which extends A, so the prototype chain is B->A->Object. Inside the constructor of B you assign a new copy of a function to DoStuff on every single instance. (This uses a lot more memory.) So when you construct a new B and call DoStuff on it, there's a function there without ever examining the prototype. So that works.

Now you define a class C extending B and the prototype chain is C->B->A->Object. Again every instance of C gets a copy of a function assigned to DoStuff. But inside that constructor we're not grabbing DoStuff from an instance of B, we're grabbing it from the prototype, and we didn't add that function directly to the prototype. Failing to find it there we go up the prototype chain to A.prototype and find a DoStuff member to use from there. That's why C has a reference to A's DoStuff.

You could make your code work as expected if you did something like this:

class B extends A {
    constructor() {
        super();
    }
}
B.prototype.DoStuff = function () {
    return A.prototype.DoStuff() + " and Called from B";
}

But that's just being silly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.