Skip to content
Advertisement

JavaScript: ‘this’ in superclass refers to subclass

I am calling a superclass method from a subclass. When called from a subclass, ‘this’ in the superclass method no longer refers to the superclass but to the subclass that called it. This creates problems with inheritance.

Details as follows:

In my project I use a class to construct objects from registered types. The types are registered from properties, or alternatively from a template based on an already defined type:

class A{

createObject(type) {
    let object = null;
    for (let i in this.objecttypes) {
        if (this.objecttypes[i].type == type) {
            if (this.objecttypes[i].template) {
                object = this.createObject(this.objecttypes[i].template);
                object.type = type;
            }
            else
                object = new TestObject(type);
            for (const aname in this.objecttypes[i].attributes)
                object.set(aname, this.objecttypes[i].attributes[aname]);
            //console.log("Object: "+JSON.stringify(object));
        }
    }
    return object;
}

}

This works fine in the superclass. However, I have also constructed a subclass with more user-friendly methods. In the subclass, the method called ‘createObject’ doesn’t return the created object. It stores it and returns the object id:

class B extends A{

createObject(type,otherargs){
    let object=super.createObject(type);
    this.objects.set(object.id,object);
    /* do things with otherargs     */
    return object.id;
}

}

/* Method to test function. In test class */

templateObjects(){
    let container=this.getContainer();
    console.log("proto: "+JSON.stringify(Object.getPrototypeOf(container)));
    let tt=App.createObjectType("template1","",[{name:"attr0",value:1},{name:"attr1",value:2},{name:"attr2",value:3}]);
    let ut=App.createObjectType("test","template1",[{name:"attr3",value:66}]);
    container.addObjectType(tt);
    container.addObjectType(ut);
    let o0=container.createObject("template1");
    console.log("Object: "+JSON.stringify(o0));
    let o1=container.createObject("test");
    console.log("Object: "+JSON.stringify(o1));
}

    

When I now try to create template-based objects from the subclass, and the superclass code comes to this point:

            if (this.objecttypes[i].template) {
                object = this.createObject(this.objecttypes[i].template);
                object.type = type;
            }

the ‘this.createObject(…)’ call results in calling the subclass’ createObject-method, thus returning a numerical key, and failing on trying to assign the type to it.

I know I could for instance rename the method in the subclass to avoid the problem, I could send in the class object (the ‘this’) as a parameter or other tricky stuff.

But is there a more straightforward, idiomatic way to solve this?

Advertisement

Answer

this refers to an object, not to a class. In this case, it’s an instance of B in normal use (even in code in A — there’s a single object). Since the createObject property of the object comes from B, that’s what you get from this.createObject (regardless of where that code occurs).

But there’s a more fundamental issue:

When you subclass something, you’re saying that it is a more specialized version of the thing you subclassed. That is, B is a specialised A. That means B is an A. A has a particular contract, e.g. it returns an object from its createObject method. By changing that return type in B, you’re breaking that contract. B is no longer an A, it’s something A-like except for createObject.

So I recommend not doing that, since it breaks one of the rules of subclassing.

You can call A‘s version of createObject from the code in A, like this:

object = A.prototype.createObject.call(this, this.objecttypes[i].template);

but I strongly recommend that you don’t. 🙂 Instead, don’t make B break A‘s contract. Use a different method for what you’re doing in B.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement