5

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?

John Hoffman
  • 17,857
  • 20
  • 58
  • 81
  • Are you sure you pasted the right code? The word `say` doesn't appear there once. – Ned Batchelder Jun 30 '12 at 01:28
  • Because there are no classes? ;) But the issue is that `this` is already the *wrong object* at that time (as well as the [[prototype]] being *too late* for `new` to utilize). –  Jun 30 '12 at 01:32
  • 2
    BTW, use console.log instead of document.write and alert. It will make your life so much easier in the long run. – hugomg Jun 30 '12 at 01:37

3 Answers3

7
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
  • 5,644
  • 3
  • 38
  • 66
3

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

Wayne
  • 59,728
  • 15
  • 131
  • 126
2

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.

hugomg
  • 68,213
  • 24
  • 160
  • 246
  • @pst: I was talking about how he was expecting the "prototype" property to behave specially. – hugomg Jun 30 '12 at 01:35
  • Okay, that's better, +1 :-) Is `Object.create(proto)` [natively] available in 3rd edition, though? –  Jun 30 '12 at 01:37
  • @pst: I can't remember now, but its very easy to write a polyfill for it when its not available: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create#Polyfill – hugomg Jun 30 '12 at 01:45
  • Someone recently added an answer to this so it showed up in my list, this is a strange way of using prototype and Object.create. Animal has instance member (namely this.name) you can "inherit" by calling `Animal.call(this,"dog");` in the Dog constructor. Animal has shared member (writeName) that should be declared as `Animal.prototye.writeName=function()` that can be inherited by Dog with `Dog.prototype=Object.create(Animal.prototype);` So as to not completely overwrite the Dog prototype every time you create a Dog instance. http://stackoverflow.com/a/16063711/1641941 – HMR Nov 12 '13 at 14:46
  • @HMR: THe reason this looks weird is because I'm using the `new` keyword to call my constructors but I'm not taking advantage of the `.prototype` magic. This was to keep things closer to the original question but I'd guess it might be clearer to go all the way and rename the functions to `make_animal` and `make_dog` and invoke them without the `new` keyword, since this way its explicit that my code isn't actually using `this` anywhere. – hugomg Nov 12 '13 at 23:03