I am trying to understand the prototype inheritance in Javascript. There have been several questions already asked on this, but they do not seem to address the confusion I am having.
How does JavaScript .prototype work?
What is the 'new' keyword in JavaScript?
There are also some great resources on the internet.
http://www.webdeveasy.com/javascript-prototype/
http://javascriptissexy.com/javascript-objects-in-detail/
When I read those resources, I think I understand prototypical inheritance, but when I try some tests in the browser console I realise I am missing something.
Based on the code example below:
(1) why is the added prototype function description() not visible on the parent object animal ?
(2) why is an object created after the description() function is added now missing the original object attributes: name and color?
Example:
Create a simple object.
function animal(){
var name;
var colour;
}
Create two new objects based on the prototype animal
> cat = new animal();
< animal {}
> cat.name = 'cat';
> cat.color = 'black';
> cat
< animal {name: "cat", color: "black"}
Add an attribute to one object only
> dog = new animal();
> dog.name = 'dog';
> dog.color = 'brown';
> dog.bite = 'hurts';
> dog
< animal {name: "dog", color: "brown", bite: "hurts"}
> animal.prototype.description = function(){
console.log('A ' + this.name + ' is ' + this.color); }
< function (){
console.log('A ' + this.name + ' is ' + this.color); }
Add a new function as a prototype. This all works as I expected it to.
> animal
< function animal(){
var name;
var colour;
}
> dog
< animal {name: "dog", color: "brown", bite: "hurts", description: function}
> cat
< animal {name: "cat", color: "black", description: function}
Here is the confusion. the new function description appears on both of the existing dog and cat objects, but it does not appear on the parent object animal
> cat.description;
< function (){
console.log('A ' + this.name + ' is ' + this.color); }
> cat.description();
< A cat is black
A new object cow created from the parent animal now has only the description function, but not the name or color
> cow = new animal();
< animal {description: function}
> cow
< animal {description: function}
EDIT
Based on #Quince's answer, I went back to the browser, but am still confused:
>animal = function(){
this.name;
}
<function (){
this.name;
}
>animal
<function (){
this.name;
}
>cat = new animal();
<animal {}
>cat
<animal {}
In this case the new object cat doesn't seem to inherit the name property from the parent.
in this example here
function animal(){
var name;
var colour;
}
name and colour are private to animal and as such can not be accessed.
now when you run this
cat = new animal();
< animal {}
> cat.name = 'cat';
> cat.color = 'black';
you are actually not setting those private name and colour attributes inside animal you are giving it new attributes called name and colour
when you run the following
animal.prototype.description = function(){
console.log('A ' + this.name + ' is ' + this.color);
}
animal is being given a publicly accessible function called description which is why you can see it when you create a cow, but actually this function will not print anything yet as the animal class does not have a name or color attribute.
options you have here are to change animal to
function animal(){
this.name;
this.colour;
}
although this gives no advantages other letting you know you can set this attributes of the animal, you will still need to set them in order for description to print anything.
Another option if wanting to keep things private and public is to use something along the lines of the revealing module pattern which allows js to keep attributes private to an object by declaring them outside of the object you return.
function Animal() {
//private artibutes
var name;
var colour;
//return publically avaliable functions/attibutes
return {
description: function () {
console.log('A ' + name + ' is ' + colour);
},
setName: function (nameIn) {
name = nameIn;
},
setColour: function (colourIn) {
colour = colourIn;
},
getName: function () {
return name;
},
getColour: function () {
return colour
}
}
};
now the only way to access the private name and color is to use the publicly available functions returned here is a fiddle showing it in action http://jsfiddle.net/leighking2/fxrLpj9v/
Oh and just realized i never actually answered the question in the title, so when you print animal you do not see the added description function as this function is attached to the prototype, animal when printed raw is just a constructor. Anything created using new Animal will inherit from animal's prototype which is why it can be seen in dog and cow
Related
I am new at learning JavaScript concepts. Want to understand how prototypical inheritance work. My impression was if your class inherits its parent, and you have a same named method in prototypes of both classes, when you call the method on child instance, the method in the child prototype will be called.
Code:
function Animal(name) {
this.name = name;
}
Animal.prototype.printName = function () {
console.log(this.name + ' in animal prototype');
}
function Cat(name) {
Animal.call(this, name);
}
Cat.prototype.printName = function () {
console.log(this.name + ' in cat prototype');
}
Cat.prototype = Object.create(Animal.prototype);
var anm1 = new Animal('mr cupcake');
anm1.printName();
var cat1 = new Cat('cat');
cat1.printName();
On calling cat1.printName() I expected it to log 'cat in cat prototype' but it logged 'cat in Animal prototype'. Could someone please explain the reason to me. Thanks.
You are correct, but your override of the printName() function is, itself, being overridden by the next line when you reset the Cat.prototype. Simply moving the order of the code fixes the issue:
function Animal(name) {
this.name = name;
}
Animal.prototype.printName = function() {
console.log(this.name + ' in animal prototype');
}
function Cat(name) {
Animal.call(this, name);
}
// OLD LOCATION of code
// This was overriding your override!
// Setting the prototype of an object to another object
// is the basis for JavaScript's prototypical inhertiance
// This line replaces the existing prototype object (which is
// where your override was) with a completely new object.
Cat.prototype = Object.create(Animal.prototype);
// NEW LOCATION
// AFTER setting the prototype (and creating inheritance),
// it is safe to do the override:
Cat.prototype.printName = function() {
console.log(this.name + ' in cat prototype');
}
var anm1 = new Animal('mr cupcake');
anm1.printName(); // "mr cupcake in animal prototype"
var cat1 = new Cat('cat');
cat1.printName(); // "cat in cat prototype"
I am trying to create a simple Animal class with a constructor and a prototype function to return an Animal's name and description.
So far, I created the constructor for the class:
class Animal {
Animal(name, description) {
this.name = name;
this.description = description;
}
}
But when I try to create an Animal prototype and call a function to return the Animal's name and description...
Animal.prototype.message = function() {
return this.name " + has the following description: " + this.description;
}
...Visual Studio Code highlights the periods in Animal.prototype.message() and tells me ';' expected.
I've been at this for an hour now and I feel like I'm missing something obvious, but regardless I would like to know what I'm doing incorrectly. Thanks in advance for any help.
EDIT: fixed code typos.
I see a couple of issues here.
In your class, you do not have a constructor (Animal should be constructor)
You are using prototype to add a function to your class. Why not just do it the right way (...the es6+ way)
I would be the goofy error you're receiving is because of the "constructor" setup (using Animal instead of constructor) or it's because you're doing
Animal.prototype.message() = function { ... } (should be Animal.prototype.message() = function() { ... })
Example:
class Animal {
constructor(name, description) {
this.name = name;
this.description = description;
}
message() {
return `${this.name} has the following description: ${this.description}`;
}
}
const animal = new Animal('liger', 'a lion and a tiger');
const message = animal.message();
console.log(message);
I'm looking into javascript inheritance for the first time, and I don't seem to be able to get it to work, or perhaps I'm not understanding it correctly.
So I have some code here, let's have a look at it:
<script language="javascript" type="text/javascript">
//object and prototype
function cat(name){//object constructor
this.name = name;//property
this.talk = function(){//method
console.log(this.name + " say meeow");
}
}
cat1 = new cat("Cat 1 Felix")
cat1.talk();
cat2 = new cat("cat 2 Joy")
cat2.talk()
//inheritance
function isleManCat(name){
cat.call(this,name)
this.feature = "no tail"
this.detail = function(){
console.log(this.name + " has " + this.feature);
}
}
isleManCat.prototype = new cat();
cat3 = new isleManCat("isle of Man cat")
cat3.talk();
cat3.detail();
</script>
So I have 2 cats objects here and cat1 and cat2 prints the expected results:
Cat 1 Felix say meeow
cat 2 Joy say meeow
. Then cat3 is a isleOfMan() cat which should inherit from cat(), and I would have thought that it would inherit the name property from cat() but it prints undefined:
undefined say meeow
undefined has no tail
Can somebody kindly let me know why it doesn't work and what i'm doing wrong please as I don't seem to understand that?
Thanks
Your third kitten deriving cat won't produce your expected result because the cat constructor won't be called by isleManCat's constructor auto-magically! And your third kitten has no name at all!
// You need to pass the name of the whole cat to this constructor
// to later give it as argument to base constructor (i.e. cat())
function isleManCat(name){
// This is calling cat constructor function setting the "this"
// keyword to the "this" of isleManCat
cat.call(this, name);
this.feature = "no tail"
this.detail = function(){
console.log(this.name + " has " + this.feature);
}
}
In ECMA-Script 5.x you can also use Function.prototype.apply and arguments reserved keyword to pass isleOfMan constructor's arguments as an array to cat:
function isleManCat(){
cat.apply(this, arguments);
this.feature = "no tail"
this.detail = function(){
console.log(this.name + " has " + this.feature);
}
}
And, in ECMA-Script 2015 and above, you can use rest parameters:
function isleManCat(...args){
// This is calling cat constructor function setting the "this"
// keyword to the "this" of isleManCat
cat.apply(this, args);
this.feature = "no tail"
this.detail = function(){
console.log(this.name + " has " + this.feature);
}
}
I wonder why based on my code, cat.getIt(); won't work. I created two objects and wanted the "cat" object to have the "dog" object as its prototype. It worked but what doesn't work is the dogs method "getIt" won't get inherited by the object cat even though I said: Cat.prototype=new Dog() (Cat.prototype=Object.create(Dog.prototype); didn't work too). Does anyone know why?
function Dog (name){
this.name=name;
this.age=[];
}
var myDog= new Dog("Jo");
Dog.prototype.sayHi=function(){
return "My name is " + this.name + ", I'm a Dog";
};
Dog.prototype.getIt = function(){
var small = {
name: "Baby " + this.name
};
this.age = [small];
return small;
}
myDog.getIt();
function Cat(name,color){
Dog.call(this,name);
this.color=color;
}
var cat = new Cat("Lexy", "red");
//Cat.prototype=Object.create(Dog.prototype); //Or should I do it like that?
Cat.prototype=new Dog();
cat.getIt();
I am trying to figure out what 'this' is referencing in my last function (Mamamal.prototype.haveBaby);
var Mammal = function(name){
this.name = name;
this.offspring = [];
};
// var myMammal = new Mammal('Joe');
Mammal.prototype.sayHello = function(){
return 'My name is ' + this.name + ", I'm a Mammal";
};
Mammal.prototype.haveBaby = function(){
debugger;
var childName = "Baby " + this.name;
baby = new this.constructor(childName); //new Cat OR new Mammal
baby.name = childName;
this.offspring.push(baby);
return baby;
};
I am not sure why the syntax
baby - new this.constructor(childName);
is this Mammal.prototype?(then constructor so it would be Mammal.prototype.constructor(childName); That's the only way I know how to set the constructor. Mammal.constructor wouldn't work.
The value of this depends on how a function is called, in your case Mammal.prototype.haveBaby.
If you call it with Mammal.prototype.haveBaby(), then this refers to Mammal.prototype.
If you call it as instance methods (which is more likely), e.g.
var mammal = new Mammal();
var baby = mammal.haveBaby();
then this refers to mammal.
But in both cases you are accessing the same property, since every instance of Mammal inherits the properties of Mammal.protoype. So this.constructor === Mammal.prototype.constructor, no matter in which situation of those two.
Read the MDN documentation for more information about this.