Veer Shrivastav
Veer Shrivastav

Reputation: 5496

JavaScript call parent function from child object

I am new to Object Oriented Programming with javascript. And trying to implement the basics that we know from other programming languages.

I have two classes, one parent and one child. Parent have some private functions and some public functions. Child will inherit parent class.

And I wish to access parent public function from child object. What I am trying to do:

function SuperClass {
    //Constructor implementation of this class

    //public functions
    return {
        parentPublicFunction: function() {
            //Public function implementation
        }
    }
    function privateFunction() {
        //Private function implementation.
    }
}

//Inheriting
childClass.prototype = Object.create(SuperClass.prototype);
childClass.prototype.constructor = childClass;
childClass.prototype.parent = SuperClass.prototype;
function childClass {
    //Call to super constructor
    this.parent.constructor.call(this, arguments);

    //Public function for childClass.
    return {
        childPublicFunction: function() {
            //function implementation
        }
    }
}

var childObj = new childClass();
childObj.parentPublicFunction();

I am getting this error when running this:

Uncaught TypeError: undefined is not a function

Am I doing something which is incorrect.? Thanks in advance!

Upvotes: 3

Views: 11804

Answers (2)

Alxandr
Alxandr

Reputation: 12431

JavaScript OOP does not work like Java's or C#, don't expect it to, and do not try to make it. The only thing that will result from so doing is misunderstandings. Instead of trying to force your old paradigms on a language that is built up entirely different, rather learn how JavaScript OOP works.

JavaScript is Object Oriented, but that doesn't mean it has classes. At least not in the way most developers coming from Java or similar languages think of classes. It does not have classical inheritance either. However, all of these can be "faked", or mimicked (which is what you're effectively trying to do). The problem with so doing, is the fact that developers coming from Java or similar see code that looks like classes and classical inheritance, so they think it's classes with classical inheritance, and expect it to behave as such, whereas it might end up working completely different.

Let's start with your SuperClass function (because that's what it is. It's a function, not a class). First of all, your code isn't even valid syntax, because your SuperClass function does not parenthesizes. The correct syntax would be function SuperClass() { ... }.

Secondly, JavaScript uses prototypes to handle inheritance between objects (not classes), and if you want to do prototypical inheritance (which some of your code suggest you want to), you can't return a new, anonymous object from the SuperClass function. Allow me to illustrate a bit what goes on here.

When you call your SuperClass function (either from the childClass function, or from any other place) using the new keyword, as in var inst = new SuperClass();, what effectively happens is the following:

  1. JavaScript creates a new blank object, which prototype is equal to the prototype-property on the function you're calling. In code this would be var temp = Object.create(SuperClass.prototype);.
  2. JavaScript calls your function with that object as the this object. In code this would be SuperClass.call(temp, <arguments>).
  3. If the function returns anything, inst is set to that value, else, it's set to the temp value.

Now, in your SuperClass function you do the following:

//Constructor implementation of this class

//public functions
return {
    parentPublicFunction: function() {
        //Public function implementation
    }
}
function privateFunction() {
    //Private function implementation.
}

What happens here is that your SuperClass method get's invoked with a this, which is an instance of SuperClass, ie. it has SuperClass.prototype in it's prototype chain. This would mean that if you for instance did this instanceof SuperClass, you would get true, as you would expect. Then you go and return a new, anonymous object, which has a property named parentPublicFunction (which just happens to be a function, but could be anything). This new object you return, is not an instance ofSuperClass. It does not have SuperClass.prototype in it's property. You've created a situation where if I do (new SuperClass()) instanceof SuperClass you get false. In other word, the instance you return from your SuperClass constructor function, does not implement your "class". This is probably not at all what you intended, which is one of the reasons you cannot just strap classical inheritance principles on JavaScript and expect things to function the same.

Now, what we have so far, is a function name SuperClass, which returns an Object without any relation to SuperClass. SuperClass has a prototype which is empty, meaning that even if you did somehow manage to create objects with SuperClass.prototype in it's prototype chain, it would not inherit anything from SuperClass, because there is nothing defined to inherit. Which brings us to the childClass function.

First off all, I'll be naming it ChildClass instead of childClass. Any function in JavaScript you expect people to call with new should start with an upper case. Any function you don't expect to be called with new should be started with a lower case. There is no way in JavaScript to know if a function is intended to be called with or without new, so this is used to signify.

Now, ChildClass.prototype is set up as a instance of SuperClass, meaning that any methods in SuperClass's prototype will be callable from an instance of ChildClass. And any instance of ChildClass is also an instance of SuperClass (just as you'd expect). However, as already explained, the prototype of SuperClass is empty, so there is nothing to inherit from it. Therefore, the only thing ChildClass gains from this inheritance is the fact that a instanceof ChildClass implies that a instanceof SuperClass. However, just as with SuperClass, you return a anonymous object from ChildClass, which again result in the fact that (new ChildClass()) instanceof ChildClass is false.

And therein, lies (most) of your problems. The solution is simply to learn how OOP works in JavaScript and embrace that when doing JavaScript, instead of trying to bend it to how you assume things to work.

Upvotes: 8

Felix Kling
Felix Kling

Reputation: 816600

Because you are returning your own objects from the constructor functions, you have to do a little more plumbing.

//Call to super constructor
this.parent.constructor.call(this, arguments);

returns the public API of SuperClass but you are not doing anything with the result. You'd have to merge it into the child's public API:

//Call to super constructor
var parentAPI = this.parent.constructor.call(this, arguments);

//Public function for childClass.
return Object.assign(parentAPI, {
    childPublicFunction: function() {
        //function implementation
    }
});

Assigning to childClass.prototype is actually irrelevant in your case, because the object you return from the constructors doesn't inherit from it anyway. I.e. childObj won't have any of the properties you assigned to the prototype (such as parent). There are even more things you miss out by returning your own object, such as being able to use instanceOf (childObj instanceOf childClass will be false).

To make that all of that work you have to assign the methods to this inside the constructor (instead of returning an object). You can find more information about that style of OOP in the MDN documentation.


Note: It is convention to start the names of constructor functions with a capital letter, i.e. ChildClass.

Upvotes: 5

Related Questions