Reputation: 7912
I am struggling to understand the constructor invocation pattern in Javascript.
I have a base object Mammal
( would it be incorrect to use the term class ? ) and an inherited object Cat
. In the following code the object Cat
correctly inherits from the Mammal object.
/*
Mammal base Object
*/
var Mammal = function(name) {
this.name = name;
}
Mammal.prototype.get_name = function() {
return this.name;
}
Mammal.prototype.says = function () {
return this.saying || '';
}
/*
Cat object
*/
var Cat = function (name) {
this.saying = "Meow";
this.name = name;
}
Cat.prototype.purr = function (number) {
var i =0, s='';
for ( i=0; i<number; i++)
if (s)
s +='-';
s+='r';
return s;
}
Cat.prototype = new Mammal();
console.log("Pseudo classical inheritance approach");
var mammal = new Mammal(" I am a mammal");
console.log("Who are you ? " + mammal.get_name());
console.log("What are you saying? " + mammal.says());
var cat = new Cat('I am a cat');
console.log("Who are you ? " + cat.get_name());
console.log("What are you saying? " + cat.says());
What I don't like in this pattern is how the constructor of the base object is used. The object Cat
does not reuse correctly the constructor of the base class Mammal
. I would like to have a more flexibility. Each time a Cat
object is created, the constructor of the Mammal
object is invoked with no arguments. I would like to use a mechanism similar to the "super" keyword in Java, so that when the constructor of Cat
is called with name
as parameter, also the constructor of Mammal
is called with name
as parameter.
I tried to implement the Cat
constructor as follows :
var Cat = function (name) {
this.saying = "Meow";
// Super (name);
this.prototype = new Mammal(name);
}
This does not work as expected. this.prototype
is undefined. why? Why this approach is completely wrong? does this
point to the newly Cat
object?
I know, there are different ways to implement inheritance in javaScript, but I am wondering if there is a way to implement the super mechanism like in Java.
Thanks. :D
Upvotes: 1
Views: 283
Reputation: 31685
this.prototype
is undefined, because no one defined it.
Cat
is a function. As such, it has a property prototype
. That's mandated by the ECMAScript standard.
this
is an object that is not a function. As such, the ECMAScript standard does not mandate that it has a prototype
property.
If this
is a Cat
(i.e. an object that was or is created using new Cat
), then it has, for the sake of specification, an internal [[Prototype]]
property which is a Mamal
. But this mamal is not accessible directly (as implied by the word internal). When you say var maru = new Cat()
, then maru.[[Prototype]]
is linked to Cat.prototype
. That's how maru
knows about future methods of mamals.
Upvotes: 1
Reputation: 1075875
Yes, I'm afraid that's not how you set up hierarchies. It's close, but there are a couple of key issues. (One of which — calling new Mammal()
to create Cat.prototype
— is a very, very, very frequent error you see in a lot of blog posts and such.)
Here's a simple example of doing it correctly:
// A function to set up the link between a child and parent
function derive(Child, Parent) {
// `ctor` is a temporary function we use so we can get an object
// backed by `Parent.prototype` but without calling `Parent`.
function ctor() { }
// Borrow the prototype
ctor.prototype = Parent.prototype;
// Create an object backed by `Parent.prototype` and use it as
// `Child`'s prototype
Child.prototype = new ctor();
// Some housekeeping to make the prototype look like the ones
// the JavaScript engine creates normally.
Child.prototype.constructor = Child;
// Note: If we can rely on ES5 features, we could use
// `Object.create` instead of the `ctor` function.
}
// The parent constructor
var Mammal = function(name) {
this.name = name;
};
// Some stuff for its prototype
Mammal.prototype.get_name = function() {
return this.name;
};
Mammal.prototype.says = function () {
return this.saying || '';
};
// The child constructor
var Cat = function(name) {
Mammal.call(this, name);
this.saying = "Meow";
};
// Hook it up to the parent
derive(Cat, Mammal);
// Add some things to its prototype
Cat.prototype.purr = function (number) {
var i =0, s='';
for ( i=0; i<number; i++)
if (s)
s +='-';
s+='r';
return s;
};
If you're interested in doing inheritance hierarchies in JavaScript, you may find my Lineage
script useful. You may or may not choose to use it, but it demonstrates how to set things up, a way to do calls to the parent's version of methods ("supercalls"), etc. In particular, this documentation page comparing using Lineage
to not using it shows how to do this without any helper script. But there's a reason I wrote a helper script to do it. :-)
Upvotes: 1
Reputation: 48314
How about
var Cat = function (name) {
this.saying = "Meow";
// Super (name);
Mammal.call( this, name );
}
Upvotes: 2