Reputation: 5496
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
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:
prototype
-property on the function you're calling. In code this would be var temp = Object.create(SuperClass.prototype);
.this
object. In code this would be SuperClass.call(temp, <arguments>)
.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
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