views:

982

answers:

5

Hi, I'been doing some inheritance in js in order to understand it better, and I found something that confuses me.

I know that when you call an 'constructor function' with the new keyword, you get a new object with a reference to that function's prototype.

I also know that in order to make prototypal inheritance you must replace the prototype of the constructor function with an instance of the object you want to be the 'superclass'.

So I did this silly example to try these concepts:

function Animal(){}
function Dog(){}

Animal.prototype.run = function(){alert("running...")};

Dog.prototype = new Animal(); 
Dog.prototype.bark = function(){alert("arf!")};

var fido = new Dog();
fido.bark() //ok
fido.run() //ok

console.log(Dog.prototype) // its an 'Object' 
console.log(fido.prototype) // UNDEFINED
console.log(fido.constructor.prototype == Dog.prototype) //this is true

function KillerDog(){};
KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")}

fido.prototype = new KillerDog();

console.log(fido.prototype) // no longer UNDEFINED
fido.deathBite(); // but this doesn't work!

(This was done in Firebug's console)

1) Why if all new objects contain a reference to the creator function's prototype, fido.prototype is undefined?

2) Is the inheritance chain [obj] -> [constructor] -> [prototype] instead of [obj] -> [prototype] ?

3) is the 'prototype' property of our object (fido) ever checked? if so... why is 'deathBite' undefined (in the last part)?

Thanks!

A: 

Thats because functions have prototype property and objects do not. prototype is a special JavaScript property.

fasih.ahmed
Well... you're circling the correct answer, but still managed to get it completely wrong. Read Triptych's answer to learn where you went off the track.
Shog9
+4  A: 

You cannot change an object's prototype once it's been instantiated with new.

In your example above, lines like

fido.prototype = new KillerDog();

simply creates a new attribute named prototype on the object fido, and sets that attribute to a new KillerDog object. It's no different than

fido.foo = new KillerDog();

As your code stands...

// Doesn't work because objects can't be changed via their constructors
fido.deathBite();

// Does work, because objects can be changed dynamically, 
// and Javascript won't complain when you use prototype 
//as an object attribute name
fido.prototype.deathBite();

The special prototype behavior applies only to constructors in javascript, where constructors are functions that will be called with new.

Triptych
You can in Mozilla (Firefox etc). But yeah that's usually impractical.
thomasrutter
+4  A: 

1) Why if all new objects contain a reference to the creator function's prototype, fido.prototype is undefined?

All new objects do hold a reference to the prototype that was present on their constructor at the time of construction. However the property name used to store this reference is not prototype as it is on the constructor function itself. Some Javascript implementations do allow access to this 'hidden' property via some property name like__prototype__ where others do not (for example Microsofts).

2) Is the inheritance chain [obj] -> [constructor] -> [prototype] instead of [obj] -> [prototype] ?

No. Take a look at this:-

function Base() {}
Base.prototype.doThis = function() { alert("First"); }

function Base2() {}
Base2.prototype.doThis = function() { alert("Second"); }

function Derived() {}
Derived.prototype = new Base()

var x = new Derived()

Derived.prototype = new Base2()

x.doThis();

This alerts "First" not Second. If the inheritance chain went via the constructor we would see "Second". When an object is constructed the current reference held in the Funcitons prototype property is transfered to the object hidden reference to its prototype.

3) is the 'prototype' property of our object (fido) ever checked? if so... why is 'deathBite' undefined (in the last part)?

Assigning to an object (other than a Function) a property called prototype has no special meaning, as stated earlier an object does not maintain a reference to its prototype via such a property name.

AnthonyWJones
Nice! Note that if you declared var x = new Derived() AFTER theDerived.prototype = new Base2() you would see 'Second'. x is simply holding a pointer to the original Derived.prototype, but that doesn't meant that we didn't in fact redirect it to Base2(). The pointers simply been changed. Not at all contradicting anything Anthony is saying but just clarifying that last bit. See my examples: http://github.com/roblevintennis/Testing-and-Debugging-JavaScript/blob/master/code/objects/lib/js_inheritance.js
Rob
Oh, and I think you meant: __proto__ not __prototype__
Rob
Markdown, ergh! underscore underscore proto underscore underscore
Rob
The point the asker is missing is that the prototype property only exists on the constructor, not instances of objects. so if you have an instance and you want to know its prototype you must callinstance.constructor.prototype. However, as noted, setting that will not change the prototype of the instance, only of the constructor..
Juan Mendes
+2  A: 
Eugene Lazutkin
+1  A: 

I know it's already been answered but, there's a better way to do inheritance. Calling a constructor just for the purpose of inheritance is not desirable. One of the undesired effects is.

function Base() {this.a = "A"}
function Child() {this.b = "B"};

Child.prototype = new Base();

Now You've added property "a" to the prototype of Child that you didn't intend to.

Here's the right way (I didn't invent this, Ext-JS and other libs use this)

// This is used to avoid calling a base class's constructor just to setup inheritance.
function SurrogateCtor() {}

/**
 * Sets a contructor to inherit from another constructor
 */
function extend(BaseCtor, DerivedCtor) {
  // Copy the prototype to the surrogate constructor
  SurrogateCtor.prototype = BaseCtor.prototype;
  // this sets up the inheritance chain
  DerivedCtor.prototype = new SurrogateCtor();
  // Fix the constructor property, otherwise it would point to the BaseCtor
  DerivedCtor.prototype.constructor = DerivedCtor;
  // Might as well add a property to the constructor to 
  // allow for simpler calling of base class's method
  DerivedCtor.superclass = BaseCtor;
}

function Base() {
  this.a = "A";
}

Base.prototype.getA = function() {return this.a}

function Derived() {
  Derived.superclass.call(this);  // No need to reference the base class by name
  this.b = "B";
}

extend(Base, Derived);
// Have to set methods on the prototype after the call to extend
// otherwise the prototype is overridden;
Derived.prototype.getB = function(){return this.b};
var obj = new Derived();

An even easier way is to add a third parameter to extend where you specify the method of the derived class so that you don't have to call extend and then add methods to the prototype

extend(BaseCtor, DerivedCtor, {
  getB: function() {return this.b}
});

Then there are many other things you could do for syntactic sugar.

Blogged about it: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Juan Mendes