javascript proto-chaining confusion on this - javascript

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

Related

How to set the constructor of an Object without changing to a new Object?

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.

How to inherit specific prototype method in JavaScript?

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).

javascript inheritance strangeness

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 }
}

Prototypal inheritance in JS and how to get parent properties

I'm trying to have properties inherit from a parent, but I'm not clear as to the right way of doing it.
Lets say I have:
var Animal = function(name){
this.offspring = [];
this.name = name;
return this;
}
Animal.prototype.createOffspring = function(name){
name = name || 'Baby '+(this.offspring.length+1);
this.offspring.push(name);
return this;
}
Now I want to add a sub prototype inherit so I don't have to manually add everything from the parent. For example, lets say I want to add a Cat based from Animal
I'd like to do this, like if it were an Animal
var pet = new Cat('Kitty');
pet.createOffspring();
Without manually having to add name and createOffspring to the Cat constructor which is really just an Animal, but with some other added functionality (like .meow() or something).
// Parent
function Animal() {
this.name = 'An animal';
}
// Some child
function Cat() {
this.speaks = 'Meow';
}
// Here comes inheritence
Cat.prototype = new Animal();
// Or like that
// but don't forget to put all inheritable fields to Animal's prototype
Cat.prototype = Object.create(Animal.prototype);
// Let 'instanceof' work. Don't forget the following line,
// because we eraese the info about constructor of Cat instances.
Cat.prototype.constructor = Cat;
// Add some custom method
Cat.prototype.meow = function() { return this.speaks; }
var cat = new Cat();
var animal = new Animal();
/// Some tests
cat.name; // A animal
animal.name; // An animal
cat.meow(); // Meow!
cat instanceof Cat; // true
cat instanceof Animal; // true
That's it?
(UPD: Error with prototype fixed)
(UPD2: Sorry. It is late night, I make a lot of mistakes.. I must go sleep)
There is also another solution, but its Chrome,FF-specific (maybe others):
// Animal and Cat functions from above, but
Cat.prototype = {
__proto__: Animal.prototype,
constructor: Cat,
meow: function() { ... }
}
Looks shorter, but not'd be tempted by this: it's better to follow ECMAScript standart.
There are a number of different patterns for implementing inheritance like you're describing in JavaScript, and they have subtle differences as far as how they treat the prototype objects.
Here's are a couple of good references on the prototype pattern and the constructor pattern to get you started.
And here's a simple implementation of what you described.

Can't inherit constructor from parent class

I have problem with inheritance of consturctor:
function Alive(name) {
this.name = name;
}
var Cat = new Function();
Cat.prototype = new Alive();
Cat.prototype.constructor = Alive;
var cat = new Cat('Thomas');
alert(cat.name);
alert show undefined. What i do wrong? jsfiddle
Looks like you want the parent constructor to get called automatically, that's not supported without some extra work. Your code should look like the following
function Alive(name) {
this.name = name;
}
function Cat(name) {
// Call the parent constructor
Alive.call(this, name);
}
Cat.prototype = new Alive();
// This line is to fix the constructor which was
// erroneously set to Alive in the line above
Cat.prototype.constructor = Cat;
var cat = new Cat('Thomas');
alert(cat.name);
If you use a library to implement inheritance, you don't have to worry about this. They can even call your parent constructor automatically if you don't want to create an empty constructor. The above code is still not ideal. See a post I wrote that talks about the 'right' way to do inheritance. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
Because Cat doesn't accept an argument. Here's what you want:
function Alive(name) {
this.name = name;
}
function Cat(name) {
Alive.call(this, name);
}
// since there's nothing on the prototype, this isn't necessary.
// Cat.prototype = new Alive();
var cat = new Cat('Tomas');
alert(cat.name);

Categories