Why does the "Cat.prototype = new Mammal()" line below not work inside the Cat() function, but it works outside the Cat() function?
function Mammal() {
Mammal.prototype.hasHair = function() { return true; }
}
alert( new Mammal().hasHair() ); // dispays true here
function Cat() {
Cat.prototype = new Mammal();
}
try {
new Cat().hasHair(); // doesn't work. why?
} catch ( err ) {
alert('error'); // displays error here
}
Cat.prototype = new Mammal(); // same line as inside Cat() function
alert( new Cat().hasHair() ); // now it works
I tried this with several different javascript implementations, so I doubt it is an implementation idiosyncrasy. I wonder about this mostly out of curiousity, but also just for organization, I would like to define about Cats inside Cat's function, not spread out all over everywhere.
At the moment you execute new Cat, an empty object which inherits from Cat.prototype is created. What is Cat.prototype? It's an empty object since you have not modified it yet.
Inside the constructor, you are assigning Cat.prototype = new Mammal();. The next instance of Cat will inherit from this Mammal instance. Creating a second instance though will also create another Mammal instance, from which the third Cat instance inherits, and so on.
You end up with this inheritance chain (catX is the x-th instance of Cat and mammalX is the x-th instance of Mammal):
cat0 -> Object
cat1 -> mammal0 -> Mammal.prototype
cat2 -> mammal1 -> Mammal.prototype
...
Each Cat instance has its own prototype, which is somehow contrary to what prototypes are used for (sharing common code). It might not look like such a big deal, but it has several consequences, including:
Increases memory usage: (nearly) each Cat instance has a corresponding Mammal instance, so you have 2N objects instead of N + 1.
Inheritance tests break: instanceof works by comparing prototypes, and since the prototype of cat0 is different than Cat.prototype (after creating multiple instances), cat instanceof Cat will yield false.
The inheritance chain you really want is:
cat0 -> Mammal.prototype
cat1 -> Mammal.prototype
cat2 -> Mammal.prototype
...
Never assign values to the prototype (or overwrite the prototype) inside the constructor function. The constructor function should only set instance specific properties (in some cases you might want to change static properties, but these are less common).
Set up the prototype outside the constructor function:
function Mammal() {}
Mammal.prototype.hasHair = function() { return true; }
function Cat() {
Mammal.apply(this, arguments);
}
Cat.prototype = Object.create(Mammal.prototype);
Cat.prototype.constructor = Cat;
Object.create creates an object inheriting from the object you pass as argument. See MDN.
Because prototype members are passed to the object instance before constructor is executed.
http://jsfiddle.net/vSDbB/
function Mammal() {
Mammal.prototype.hasHair = function() { return true; }
}
function Cat() {
Cat.prototype = new Mammal();
}
new Cat();
alert(new Cat().hasHair());
So the code above will work, but if you commented first new Cat(); line it wouldn't since on the new Cat().hasHair() line Cat's prototype doesn't have hasHair() method
I'm slightly confused. What do you actually expect?
You're assigning a function into the prototype chain from Cat, but you do so on invocation of the constructor function. If you call it like new Cat().hasHair(), the object of invocation doesn't know about that function at all.
You need to extend the .prototype before you actually invoke the constructor function.
I edited your code so it can achieve your goal. The following code creates a base class then a sub class in the same style you are attempting to do it.
function Mammal() {
this.hasHair = function() { return true; }
}
alert( new Mammal().hasHair() ); // dispays true here
function Cat() {
Mammal.call(this);
}
try {
alert( new Cat().hasHair() ); // dispays true here
} catch ( err ) {
alert('error'); // should not fire
}
because you are overwriting the prototype on every instantiation. Javascript doesn't really have inheritance.
when you do
function Mammal() {
Mammal.prototype.hasHair = function() { return true; }
}
new Mammal().hasHair();
it means
use Mammal as a constructor and instantiate a new Mammal type object
overwrite the prototype of Mammal every time you do this
but if you do this:
function Cat() {
}
Cat.prototype = new Mammal();
new Cat().hasHair();
it means
let the Cat constructor's prototype be a new Mammal type object
instantiate a new Cat type object that 'inherited' a hasHair() method through the Mammal 'class'
hope this helps.
The prototype of a function (that acts as a class), is what a new object is copied from when it's created. You shouldn't be setting properties on the prototype in the constructor.
Either this:
function Mammal() {
}
Mammal.prototype.hasHair = function(){ return true };
OR this:
function Mammal() {
this.hasHair = function(){ return true }
}
Related
While going over below tutorial code
Animal = function(name) {this.name = name}
Animal.prototype.eats = function(){
return this.name + ' is eating'
}
Chordate = function(name){Animal.call(this,name)}
I understand how call works (basically, in this case, this becomes this)... but my question is, how does one use this?
I am sorry, I understand how prototype works.. But really, I don't understand once you setup Chordate as above.. how does one use it?
How is this useful? or How are you now suppose to specify the this?
Can someone please explain w/ example?
Create a link to Animal's prototype methods:
Chordate.prototype = Object.create(Animal.prototype)
Then new it up:
var c = new Chordate('my name');
c.eats();
The line Animal.call(this,name) is like making a call to a base constructor. It executes the Animal constructor function and passes in name, but uses the correct this context:
Animal = function(name) {
// "this" will be your Chordate instance
this.name = name
}
Lets imagine youre constructing an Animal:
new Animal();
and during construction it draws a new animal onto the canvas. The constructor would look like this:
function Animal(){
canvas.draw(this.x,this.y,this.image);
}
Now youve got a tiger. The tiger should roar if it is constructed.
function Tiger(){
this.roar();
}
And now? Its an animal so its added to the canvas right? No. Because of js inheritance system, you need to do this manually. So when the tiger gets constructed, you also need to construct it as an animal:
Animal.call(this);
This is even easier with the new class syntax:
class Animal{
constructor(){
this.draw(this.x,this.y,this.image);
}
}
class Tiger extends Animal{
constructor(){
this.roar();
super()//construct Animal
}
}
This is just meant to be an addition to the other answers, and would be too long for a comment.
Perhaps it helps you to understand what the new operator actually does:
var newInstance = new ConstructorFunction(arg1, arg2);
Create a new object. The prototype of this object is ConstructorFunction.prototype:
var newInstance = Object.create(ConstructorFunction.prototype);
Call the ConstructorFunction with the newly created object:
ConstructorFunction.call(newInstance, arg1, arg2);
If ConstructorFunction inherits from another "class", it has to call its super constructor. That's what the following code does:
function Parent () {
// this === newInstance
}
function Child () {
// this === newInstance
Parent.call(this); // call the super constructor
}
Child.prototype = Object.create(Parent.prototype);
var newInstance = new Child();
// or:
var newInstance = Object.create(Child.prototype);
Child.call(newInstance); // call the constructor, which calls the super constructor
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.
I tried to find the answer, but it's hard to find a good one.
Probably the problem is well-known, but please help me.
I cannot make this work:
function Animal() {}
function Cat() {}
Animal.prototype.walk = function() { return 'I can walk' }
Animal.prototype.swim = function() { return 'I can swim' }
If I write:
Cat.prototype = new Animal();
Cat inherits walk and swim method, so what should I write to make Cat inherit only walk method?
You could assign a prototype object (*.prototype) another Object Literal ({}) but be careful for inheritage does not work properly anymore:
function Person() {}
Person.prototype.walk = function() { return 'I can walk' }
Person.prototype.swim = function() { return 'I can swim' }
function Man() {}
// select methods Man gets inherited here
// just assign certain methods from Person.prototype onto Man.prototype
// therefore Man.prototype inherites only walk method
Man.prototype = {
walk: Person.prototype.walk,
// so swim is not passed onto Man.prototype
// swim: Person.prototype.swim
};
var m = new Man();
// this throws an error
// because a object of type Man does not have got a swim method inherited
// m.swim(); Error!
console.log('is false: ', m instanceof Person);
console.log('parent constructor is Object: ', m.__proto__.__proto__.constructor);
But as you can see some checks to make sure what instance this object is and what super parent constructor it inherits some methods from do not work properly but they should work.
So you are better off by using inheritage in the right way:
function Person() {}
Person.prototype.walk = function() { return 'I can walk' }
Person.prototype.swim = function() { return 'I can swim' }
function Man() {}
Man.prototype = Object.create(Person.prototype);
var m = new Man();
// in this way Person gets every method that is assigned onto Person.prototype
// so swim method is available and can be used by objects of type Man now:
m.swim();
console.log('is true: ', m instanceof Person);
console.log('parent constructor is Person: ', m.__proto__.__proto__.constructor);
So that like instanceof operator and referenceing to a super parent constructor work properly. In this way all methods are assigned at once onto Person but by introducing additional abstract or parent constructors this might be avoidable.
Hope this helps.
I think the main problem is not how to make that cat inherit walk and not swing: I think that is impossible in the way you show, but the issue here is a not complete understanding of the inheritance hierarchy.
Look, basically what are you saying is: A Cat is an Animal, so it doesn't make sense that he doesn't inherit ALL the Animal behavior. Because if you say: An Animal is defined as someone that can WALK and SWING, and then you say: A Cat is a type of Animal, then the Cat MUST WALK AND SWING.
I think in your case you need to reorganize the hierarchy. Maybe you can make a LandAnimal, and a WaterAnimal, and a Cat will be a LandAnimal, and a Fish will be a WaterAnimal. Now, if you add a Duck, well, again you have to redefine because a Duck is a WaterAnimal (he can SWING) AND a LandAnimal (he can WALK).
please clarify the difference b/w this two codes
function Person(gender) {
this.gender = gender;
alert('Person instantiated');
}
Person.prototype.sayHello = function()
{
alert ('hello');
};
var person1 = new Person('Male');
var person2 = new Person('Female');
// call the Person sayHello method.
person1.sayHello()
and the second one is below where function define inside funciton (without prototype property)
function Animal(gender) {
this.gender = gender;
alert('animal instantiated');
this.sayToodle = function()
{
alert ('GOOOOOOO!!');
};
}
var Animal1 = new Animal('Male');
var Animal2 = new Animal('Female');
Animal1.sayToodle();
my more simple question are:
what is the difference?
define method inside or out side of a function. what is the effect?
if both same then which is the fine way to define this.
and what does prototype do?
Can we not define method of a obj out side of its function(CLASS)???
Defining a member inside the constructor (such as: this.name) gives only that instance of the object access to that member.
Defining a member inside the prototype allows all instances to "share" that property.
A way that helped me understand this was to define an array (or any other member that is not a method) inside the prototype, like so:
function Animal() {}
Animal.prototype = {
kids: [],
giveBirth: function() {
for(var i = 0; i < arguments.length; i++) {
this.kids.push(arguments[0]);
}
}
}
var cat = new Animal();
var giraffe = new Animal();
cat.giveBirth('kitten','another kitten','yet another kitten');
// cat.kids === ['kitten','another kitten','yet another kitten'];
// giraffe.kids === ['kitten','another kitten','yet another kitten'];
If you notice, the giraffe's kids were set (as kittens). Why is this? Because in this case, .giveBirth() accesses the prototype's kids array, which is shared by all instances.
What if we don't want to share the members, because the members are unique? You can do like so:
function Animal() {
this.kids = [];
}
Animal.prototype = {
giveBirth: function() {
for(var i = 0; i < arguments.length; i++) {
this.kids.push(arguments[0]);
}
}
}
var cat = new Animal();
var giraffe = new Animal();
cat.giveBirth('kitten');
// cat.kids === ['kitten']
// giraffe.kids == undefined
giraffe.giveBirth('baby giraffe');
// cat.kids === ['kitten']
// giraffe.kids === ['baby giraffe']
As you pointed out in the comments, part of how you decide to define the properties plays into memory usage; another part plays into what members you want to be "shared" across all instances.
To get a little more insight into prototypes (through understanding how new works), see What is the 'new' keyword in JavaScript?, on StackOverflow.
Here's a quote from there:
After a lot of searching, I have finally found out exactly what the
new keyword does, and it is 4 things:
It creates a new object. The type of this object, is simply object.
It sets this new object's internal, inaccessible, [[prototype]] property to be the constructor function's external, accessible,
prototype object.
It executes the constructor function, using the newly created object whenever this is mentioned.
It returns the newly created object, unless the constructor function returns a non-primitive value. In this case, that
non-primitive value will be returned.
Prototype members are shared among instances and members in the constructor function defined as this.something are instance specific.
When an instance need instance specific members (like Person.name) define it as this.name. When it can be shared (like a method sayName) define it on the prototype like: Person.prototype.sayName=function(){...
For more info on prototype and constructor functions you can check this answer.
I am trying to create a new class Dog that inherits via prototypical inheritance from the Animal class:
function Animal() {
this.name = "animal";
this.writeName = function() {
document.write(this.name);
}
}
function Dog() {
this.name = "dog";
this.prototype = new Animal();
}
new Dog().writeName()
JS Fiddle
However, I get a Javascript error: Uncaught TypeError: Object #<Dog> has no method 'say'.
Why? Shouldn't the Dog object retain an Animal object as a prototype?
function Animal() {
this.name = "animal";
this.writeName = function() {
document.write(this.name);
}
}
function Dog() {
this.name = "dog";
}
Dog.prototype = new Animal();
dog = new Dog();
dog.writeName();
now dog has all of the properties of animal.
jsfiddle
#ryan's answer is correct, of course, but he doesn't really say what's different about it and it might not be clear to a beginner, so...
The mistake you're making is that this.prototype = new Animal(); assigns an Animal instance to a property named prototype on the current Dog instance (referred to by this), but there's nothing special about a property named prototype in this context.
The prototype property is only magical on function objects. When you create a new instance of SomeFunc using new SomeFunc() that new object's internal/hidden [[prototype]] pointer will refer to the object pointed to by SomeFunc.prototype. The prototype name isn't special in any other context.
The "prototype" property is just a regular property. The real [[Proto]] property that deals with delegation is hidden and can't be directly manipulated after an object is created (except with some extensions: in Firefox, its the __proto__ property).
A correct Javascript inheritance example that is similar in spirit to what you are doing would use Object.create to create a dog with the correct [[Prototype]] property:
function Animal() {
this.name = "animal";
this.writeName = function() {
document.write(this.name);
}
}
function Dog() {
var dog = Object.create(new Animal())
dog.name = "dog";
return dog;
}
(new Dog()).writeName()
A more idiomatic example would be something like ryan's answer, although I would recommend using Object.create instead of new Animal to instantiate the dog prototype and I would put the animal methods in a separate animal prototype instead of manually attaching them in the constructor like you are doing.