What is wrong with this code? Can somebody help me with JavaScript Object Inheritance? I am starting to feel like an idiot!!
Thanks in advance,
Sam
function Human(name, sex) {
this.name = name;
this.sex = sex;
};
function Man(name) {
Man.prototype = new Human(name, "Male");
};
var m = new Man("Sam Striano");
alert(m.name); //<-- = Undefined
You want this instead:
function Man(name) {
Human.call(this, name, "Male");
}
What that code does
It seems like you're only trying to call the constructor of the parent, Human, which isn't the same is prototypal inheritance. The code above takes the constructor for Human and applies it to this - a new Man object.
What your code does
The line Man.prototype = new Human(name, "Male") is changing the prototype of Man every time a new Man is created. Not only that, you're completing re-assigning the prototype object, and so it will only apply to objects created after that assignment - i.e. not the first one. Hence, m.name is undefined.
Proper prototypal inheritance
Note that calling the parent's constructor, as in my code above, won't cause Man to automatically inherit any methods assigned to Human.prototype. The best way to do this is to clone Human.prototype into Man.prototype but outside of any constructors. Like this:
function Man(name) {
Human.call(this, name, "Male");
}
function inherit(parent, child) {
if (Object.create) child.prototype = Object.create(parent.prototype);
else {
for (var key in parent.prototype) {
if (!parent.prototype.hasOwnProperty(key)) continue;
child.prototype[key] = parent.prototype[key];
}
}
}
inherit(Human, Man);
This may seem rather verbose, and the alternative may be to do this:
Man.prototype = new Human('no name', 'Male');
Which will work, but causes unwanted side-effects since we're forced to assign a dud name to the prototype, and it's letting the constructor for Human call an extra time just for assigning the prototype. Be warned if you go down this path and later change the Human constructor to do more than just assign properties to this.
There's usually two steps to mimic classical inheritance in javascript:
Your subclass constructor needs to call the parent constructor
Your subclass prototype needs to chain into the parent prototype
the first step usually looks like
function Subclass(blah) {
ParentClass.apply(this, arguments);
}
The second step is trickier. On JS environments that implement the __proto__ property, you could do
Subclass.prototype = {
__proto__ : ParentClass.prototype,
subclassMethod1: function() { /* ... */ }
}
Unless you know exactly where your script will run (like in a node.js environment), you can't rely on __proto__ being available to your script, so the general approach will require to use Crockford's object() method:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
Subclass.prototype = Object.create(ParentClass.prototype);
Subclass.prototype.subclassMethod1 = function() { /* ... */ }
That's the gist of it. ES5 runtimes may have Object.create() already built-in, and that's fine.
There are leftover things to complete the illusion of classic inheritance, such as the ability to easily call a parent class' overriden method. With what we have now, you'd need to have something like ParentClass.prototype.overridenMethod.call(this, arg1, arg2) in your Subclass method.
Some OO libraries will helpfully define extra cruft on each of your subclass instances so you can use things like this.superclass.overridenMethod(arg1, arg2).
The implementation of that cruft is left as an exercise to the reader ;)
I think what you're after is to have the Man class in inherit properties from Human. You're on the right track, but would need to apply a new Human instance once as the prototype object of Man.
function Human(name, sex) {
this.name = "some default";
this.sex = sex;
};
function Man(name) {
if( name !== undefined )
this.name = name;
};
Man.prototype = new Human(name, "Male");
Man.prototype.constructor = Man;
var m = new Man("Sam Striano");
alert(m.name); // alerts "Sam Striano"
alert(m.sex); // alerts "Male"
As far as I know, you should handle all stuff with prototype and constructor and the inerithance could be managed in this way:
// Define superclass
function Human( name, sex ) {
this.name = name;
this.sex = sex;
}
// Define superclass methods
Human.prototype.method1 = function() {
alert( 'This is the call to ORIGINAL method1() with name: ' + this.name + ' and sex: ' + this.sex );
}
// Define subclass
function Man( name, age ) {
this.constructor.apply( this, [ name, 'Man' ] );
this.age = age;
}
// Define subclass inerithance
Man.prototype = new Human();
// Define subclass methods
Man.prototype.method1 = function() {
alert( 'This is the call to OVERWRITE method1() with name: ' + this.name + ' and sex: ' + this.sex + ' and age: ' + this.age );
this.constructor.prototype.method1.apply( this );
}
var m = new Man( 'Sam Satriano', 30 );
m.method1();
// Should alert:
// This is the call to OVERWRITE method1() with name: Sam Satriano and sex: Man and age: 30
// This is the call to ORIGINAL method1() with name: Sam Satriano and sex: Man
Hope this helps. Ciao!
Without getting into an inheritance fight, your problem can be solved by changing your code to the following:
function Human(name, sex) {
this.name = name;
this.sex = sex;
};
function Man(name) {
// This is how you call the parent's constructor
Human.call(this, name, "Male");
};
// The call to setup the prototype only needs to happen once
// Not in every instantiation of the object
Man.prototype = new Human();
// Have to fix the constructor, right now it's set to Human
Man.prototype.constructor = Man;
var m = new Man("Sam Striano");
>> m.name // outputs "Sam Striano";
>> m instanceof Human // outputs true
This is still not an ideal way to inherit. I posted something explaining what makes good JS inheritance. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
Related
In this related question, the answer for setting a constructor is like so:
function OldClass() {
alert("old class with incomplete constructor")
}
function ChangedClass(){
alert('new class with same prototype and more complete constructor');
}
ChangedClass.prototype = OldClass.prototype;
var theClassIWant = new ChangedClass();
This is not really setting the constructor, it's making a new object and inheriting the prototype. How can I set the constructor of an Object without changing to a new Object? Is it possible?
P.S. I want something like this:
//non working code
var Foo = function() {}
Foo.prototype.constructor = function(bar_val) {
this.bar = bar_val;
}
var fez = new Foo("cat")
console.log(fez.bar); // outputs: cat;
constructor is a property of the constructor function's prototype, which points to the function itself by default.
When you modify Foo.prototype.constructor, this property points to another function. However, the constructor function Foo remains. So when you create an object, you are still instantiate the constructor function Foo, not the one Foo.prototype.constructor points to.
This is why your code doesn't work. Then let's talk why we need to change the constructor and how to.
In ES5 and before, we take advantage of prototype to simulate a class inheritance mechanism. Which looks like this:
// parent class
function Person(name) {
this.name = name
}
// define parent method
Person.prototype.say = function() {
console.log(this.name)
}
// child class
function Coder(name, major) {
Person.call(this, name) // inherits parent properties
this.job = 'Coding'
this.major = major
}
// inherits properties and methods from parent prototype
Coder.prototype = new Person()
// define child method
Coder.prototype.work = function() {
console.log('I write ' + this.major)
}
// instantiate a child object
var me = new Coder('Leo', 'JavaScript')
console.log(me) // {name: "Leo", job: "Coding", major: "JavaScript"}
Everything looks perfect, but there a problem:
console.log(me.constructor) // Person
What? Isn't me constructed with Coder? Why?
Go back to the first line of this answer, read it again: constructor is a property of the constructor function's prototype.
Originally Coder.prototype.constructor is Coder itself. But with this line: Coder.prototype = new Person(), it got changed. Coder.prototype.constructor now equals Person.prototype.constructor, which is Person.
Well, some would say, try instanceof. Yes, that works:
me instanceof Coder // true
But it's also true for Person, because Coder is a sub class of it:
me instanceof Person // true
This doesn't make sense so we need to fix the constructor problem. That's why we use Coder.prototype.constructor = Coder after Coder.prototype = new Person(), to get back the original constructor.
Full code looks like this:
// parent class
function Person(name) {
this.name = name
}
Person.prototype.say = function() {
console.log(this.name)
}
// child class
function Coder(name, major) {
Person.call(this, name) // inherits parent class
this.job = 'Coding'
this.major = major
}
Coder.prototype = new Person() // inherits parent's prototype
Coder.prototype.constructor = Coder // get back constructor
Coder.prototype.work = function() {
console.log('I write ' + this.major)
}
myClass = {
constructor: function(text){
console.log(text);
}
}
let ob = Object.create(myClass);
ob.constructor("old constructor "); //Output: old constructor
myClass.constructor = function(){
console.log("new constructor ")
}
ob.constructor(); //Output: new constructor
You can do this with every property or function of a class.
Watch this Video here. It explains it perfectly.
Usual way to implement inheritance in javascript is something like:
function Person(name, surname) {
this.name = name;
this.surname = surname;
}
Person.prototype.whoAmI = function() {
console.log("I'am " + this.name + " " + this.surname);
}
function Ninja() {
Person.apply(this, arguments); // call to parent constructor
}
Ninja.prototype = new Person();
Ninja.prototype.constructor = Ninja;
var ninja = new Ninja("John", "Doe");
ninja.whoAmI();
From backbone what i can see is use of "Surrogate" function like: (very simplified example of what i can extract like example from Backbone source code)
function Person(name, surname) {
this.name = name;
this.surname = surname;
}
Person.prototype.whoAmI = function() {
console.log("I'am " + this.name + " " + this.surname);
}
function Ninja() {
Person.apply(this, arguments);
}
var Surrogate = function() { this.constructor = Ninja; }
Surrogate.prototype = Person.prototype;
Ninja.prototype = new Surrogate();
var ninja = new Ninja("John", "Doe");
ninja.whoAmI();
From what i can understand, these examples work exactly same so why need for Surrogate function.
I find one comment in the source about this:
Set the prototype chain to inherit from parent, without calling
parent's constructor function.
Why not calling parent constructor function?
Why not calling parent constructor function?
Because we want to constructor function to be only called when an instance is created. The Ninja.prototype however is not an instance of Person, it should not have name or surname properties - it should only inherit the whoAmI method. See also What is the reason to use the 'new' keyword at Derived.prototype = new Base for details.
Usual way to implement inheritance in javascript is something like
Only "something like". The correct way is not to call the parent constructor, but we don't need that Surrogate thingy for that. The standard is just to use Object.create:
Ninja.prototype = Object.create(Person.prototype);
Ninja.prototype.constructor = Ninja;
(for compatibility with ES3 environments just shim Object.create instead of littering your source code with Surrogate functions)
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
How about
var Cat = function (name) {
this.saying = "Meow";
// Super (name);
Mammal.call( this, name );
}
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. :-)
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.
Ok so we're trying to get prototype inheritance working the way we want it to, I've read a few examples, but one requirement we wanted was that we could call methods on the parent class easily. And we want to follow the module pattern + jQuery boilerplate style where we have defaults, a non-empty constructor function and prototype functions
;(function($, window, undefined){
"use_strict";
var defaultsHuman = {
id: 1,
age: 0
};
function Human( options ){
this.options = $.extend(defaultsHuman, options || {});
this.age = this.options.age;
this.gender = 'male';
//save originals for ref
this._defaults = defaultsHuman;
};
Human.prototype = {
_className: 'Human',
init: function(){
console.log('My class is ' + this._className + ' my gender is ' + this.gender + ' and my age is ' + this.age);
}
};
//Right now Human's function prototype's constructor is Object(), but IE8 uses constructor.prototype
//well now it's Object which is native so it's undefined?, anyways we lose the original reference to the constructor from the instance
//so lets reset it to the constructor - constructor is now enumerable!
Human.prototype.constructor = Human; //note this is cyclical!
//END fn Human
var defaultsChild = {
name: ''
};
//we want to create a new constructor B that has properties, its constructor prototype is an instance of Human
function Child( options ){
//merge the parent defaults with my defaults, then extend dynamic options on top
this.options = $.extend(this.constructor.prototype._defaults, defaultsChild, options || {});
this.name = options.name;
//A.call(this);
};
//new Human() calls Human's constructor and returns an object with prototype set to Human.prototype
Child.prototype = new Human();
$.extend(Child.prototype, {
school: 'St. Peter\'s',
init: function(){
//create reference to super class
this._super = this.constructor.prototype;
//this._super.init.call(this);
this._super.init();
console.log('My name is ' + this.name + ' and my school is ' + this.school);
}
});
Child.prototype.constructor = Human;
//END Child
//export modules - old method before define
window.Human = Human;
window.Child = Child;
})(jQuery, window, undefined);
//some other closure somewhere where it is required in
;(function(window, undefined, Human, Child){
"use_strict";
var me = new Child({
name: 'Clarence',
age: 7
}).init();
})(window, undefined, Human, Child);
What is confusing me is that in Human's init function the this refers to a Human instance, but in a state as if the Human constructor never ran, so gender which is statically set to male isn't even there.
My class is Human my gender is undefined and my age is undefined
My name is Clarence and my school is St. Peter's
I can easily fix this by calling this._super.init.call(this); instead, which I'll probably just do, but I am still curious.
I explicitly set the function prototype of Child to a complete Human object after the constructor had run: Child.prototype = new Human(); when I inspect the final instance of child me the prototype is Human where the constructor had run (as expected), but inside the Human init the this variable is such that the Human constructor had never run.
When I reference my super: this._super = this.constructor.prototype; is this not a reference to the prototype declared here Child.prototype = new Human();? And when I call this.super.init() is it not running in the context of what was returned by new Human()?
Also please note I am avoiding proto for compatibility with IE8
Not sure if I understand this correctly but you can do the following:
function Human( options ){
this.options = $.extend(defaultsHuman, options || {});
this.age = this.options.age;
this.gender = 'male';
console.log("whaat",this.age);
//save originals for ref
this._defaults = defaultsHuman;
};
function Child( options ){
Human.call(this, options);
};
Child.prototype = Object.create(Human.prototype);
function Human( options ){
this.options = $.extend(defaultsHuman, options || {});
this.age = this.options.age;
this.gender = 'male';
//save originals for ref
this._defaults = defaultsHuman;
};
If you want to support IE 8 and below or older browsers that don't have Object.create you can use the polyfil or check out this answer that has a helper function for inheritance with constructor functions.
If I call Human.prototype.init there is no instance value for this. This will point to Human.prototype instead.
this.constructor === Human;
this._super === this.constructor.prototype === Human.prototype;
this._super.init === Human.prototype.init;
If you want to use a default Human value on the Child.prototype than you should know that Hukman is shared for all instances of Child. If you then want to call the init on that you can do so like this:
Child.prototype = Object.create(Human.prototype);
Child.prototype.humanInstance = new Human();
//... in the Child constructor body:
this.humanInstance.init();
OK, I've revised most of the techniques to implement inheritance in JavaScript OOP.
As a Java programmer, I'm interested in the classical approach but here's the problem; say I want to create the Animal class (I know it's not a real class, but let me use this term) like this:
function Animal(name){
this.name = name;
}
Animal.prototype.getName = function() {
return this.name;
}
It is important to note that this is a concrete class in my first intention, I want to instantiate it, not just use it as a superclass. I may create several Animal instances, each one with its own name.
A possible way to extend this class is to do the following:
function Cat(name, owner) {
this.name = name;
this.owner = owner;
}
// ALTERNATIVE 1:
Cat.prototype = Object.create(Animal.prototype);
// ALTERNATIVE 2:
Cat.prototype = new Animal('LOLA');
// END OF ALTERNATIVES
Cat.constructor.prototype = Cat;
Cat.prototype.jump = function() {
alert(this.name + " jumping");
}
With the ALTERNATIVE 1 we just inherit the methods of the superclass, in fact we need to redefine the name property in the Cat. With the ALTERNATIVE 2 nothing actually changes, we just have one more object in the chain that holds a name property that's quite useless: it's the same for all the Cat instances.
The point here is that I've written the Animal class with its own name and I just throw it away as soon as I extend it. What I'd like to have though is a way to inherit both properties and methods and, most of all, I'd like to be able to reuse the Animal constructor.
The traditional way to inherit the properties of the base constructor is as follows:
function Cat(name, owner) {
Animal.call(this, name); // call the base constructor
this.owner = owner;
}
Cat.prototype = new Animal;
Cat.prototype.constructor = Cat;
Cat.prototype.jump = function () {
alert(this.name + " jumping");
};
The above code is equivalent to the following class in other languages:
class Cat extends Animal {
constructor(name, owner) {
super(name);
this.owner = owner;
}
jump() {
alert(this.name + " jumping");
}
}
The new way to inherit properties is exactly the same, save that we replace new Animal with Object.create(Animal.prototype). The reason we prefer the new way is because:
Calling new Animal is unnecessary overhead. The Cat constructor calls it again anyway.
Calling new Animal might not return a blank object. It might add some properties to the object.
We don't know what arguments to call new Animal with yet. Hence it makes no sense to call it.
Thus the preferred way of inheritance is now:
function Cat(name, owner) {
Animal.call(this, name); // call the base constructor
this.owner = owner;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.jump = function () {
alert(this.name + " jumping");
};
Note that it's important to call the base constructor because it may do some initialization which is necessary for the instance to work properly.
If you're interested in writing JavaScript code in a classical style then take a look at the following answer which describes prototype-class isomorphism. The following code it taken from the above answer:
function CLASS(prototype, base) {
switch (typeof base) {
case "function": base = base.prototype;
case "object": prototype = Object.create(base, descriptorOf(prototype));
}
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function descriptorOf(object) {
return Object.keys(object).reduce(function (descriptor, key) {
descriptor[key] = Object.getOwnPropertyDescriptor(object, key);
return descriptor;
}, {});
}
Using the CLASS function we can define pseudo-classes in JavaScript as follows:
var Animal = CLASS({
constructor: function (name) {
this.name = name;
},
getName: function () {
return this.name;
}
});
var Cat = CLASS({
constructor: function (name, owner) {
Animal.call(this, name);
this.owner = owner;
},
jump: function () {
alert(this.name + " jumping");
}
}, Animal);
There are other ways to do inheritance in JavaScript as well. I suggest you read my blog post on Why Prototypal Inheritance Matters to understand more about inheritance in JavaScript.