Reputation: 119
I start with defining 2 independent constructors.
var Sword = function (attack,price){
this.attack = attack;
this.price = price;
};
var LegendarySword = function (){
this.instantKill = true;
};
These constructors create objects which are obviously not related to each other.
var sword = new Sword(1, 100);
console.log(sword);
//Object
attack: 1
price: 100
__proto__: Object
var superSword = new LegendarySword();
console.log(superSword);
//Object
instantKill: true
__proto__: Object
I now want all instances of "class" LegendarySword to have properties which are characteristic of Sword "class", i.e. attack and price values. This is because all legendary swords are swords in the end, but with some unique properties (like "instantKill").
So, after reading about prototypes and inheritance, I figured out I can do like this with a smile:
LegendarySword.prototype = new Sword();
The way I understand this line is that I am using Sword's constructor in addition to what LegendarySword already has when I am constructing new objects of LegendarySword class. I expect the LegendarySword objects to gain attack and price attributes, but at the same time I do not expect Sword objects to gain instantKill attribute.
I also expect that I will be able to provide arguments in LegendarySword constructor now:
var superSword2 = new LegendarySword (5, 1000);
I would expect this to take the arguments, just like Sword constructor does, but it obviously does not:
console.log(superSword2.attack); // results to undefined (which proves that it has them dug in somewhere in the __proto__, which is so indeed)
It looks like I can still get the problem resolved approaching it from a different end. If I somewhat, as I believe, unnecessarily complicate the constructors and add new methods to Sword and call those methods and pass arguments when creating a new Legendary Sword object:
var Sword = function (attack,price){
this.attack = attack;
this.price = price;
this.setAttack = function(attack){
this.attack = attack;
};
this.setPrice = function(price){
this.price = price;
};
};
var LegendarySword = function (attack, price){
this.instantKill = true;
this.setAttack(attack);
this.setPrice(price);
};
LegendarySword.prototype = new Sword();
var sword = new Sword(1, 100);
console.log(sword);// gives correct object attributes
var superSword = new LegendarySword(10,5000);
console.log(superSword); // gives correct object attributes
But this is really ugly and my question is why cannot Javascript figure out that new attributes need to be added when I am adjusting a prototype of a constructor? This would be so intuitive to me.
Is there may be an alternative way to solve the problem?
Upvotes: 1
Views: 69
Reputation: 521
To solve you problem you just need to do:
var Sword = function (attack,price){
this.attack = attack;
this.price = price;
};
var LegendarySword = function (attack, price){
this.instantKill = true;
Sword.call(this, attack, price)
};
var sword = new Sword(1, 100);
console.log(sword);// gives correct object attributes
var superSword = new LegendarySword(10,5000);
console.log(superSword); // gives correct object attributes
So in this case you don't even need to use prototypes: you just call the parent function inside the children and pass new this.
The basic solution for inheritance in JS is:
function basicInherit(Child, Parent) {
var F = function () { };
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.super = Parent.prototype;
}
Sometimes you may not want to call constructor of parent class. Also we cannot just do Child.prototype = Parent.prototype, because if we change child prototype it will reflect in parent prototype. So we copy prototype from Parent to empty F class and then inherit Child from F.
It will make sense to use such inheritance if you want to inherit prototype properties, for example:
Sword.prototype.setPrice = function (price) { this.price = price; }
But anyway - call constructor in new child class or not - it's up to you.
Don't do such things in JS:
var Sword = function (attack,price){
this.attack = attack;
this.price = price;
this.setAttack = function(attack){
this.attack = attack;
};
}
Because every time when you will create a new instance it will create a new instance of setAttack function.
So, it will be better to put it in prototype
Upvotes: 1
Reputation: 147363
I would expect this to take the arguments, just like Sword constructor does, but it obviously does not:
You shouldn't expect that, modifying a constructor's prototype doesn't modify the constructor itself.
> console.log(superSword2.attack);
> // results to undefined
> // (which proves that it has them dug in somewhere in
> // the __proto__, which is so indeed)
It doesn't prove that. You don't know if the property exists or not since accessing a non–existent property also returns undefined (which is what is actually happening in this case).
When you do:
LegendarySword.prototype = new Sword();
You simply replace the default prototype of LegendarySword with an instance of Sword, it doesn't modify the LegendarySword itself so attack and price properties aren't added by the constructor.
If you want instances of LegendarySword to have the same own properties as Swords, you can do:
var LegendarySword = function (attack, price){
// Add Sword properties
Sword.call(this, attack, price);
// Add LegendarySword properties
this.instantKill = true;
};
Which sets up the required prototype chain and initialises the LegendarySword with Sword properties.
Upvotes: 2