SemperCallide
SemperCallide

Reputation: 2080

Method added to object via .prototype isn't being inherited?

I am a total newbie to Javascript, and this is a basic question regarding inheritance.

In the website I'm looking at, adding a new method to an object's prototype looks very simple. Here is the method they are showing:

function Gadget(name, color) { 
   this.name = name; 
   this.color = color; 
}

Gadget.prototype.getInfo = function() { 
   return 'Rating: ' + this.rating + ', price: ' + this.price;
};

However trying to replicate the same thing, I am getting an error:

(function() {

    window.onload = function() {
        document.getElementById("main").innerHTML = getMessage();
    }

    function Animal(){
        this.speak = function(){
            return "I am a " + this.species + ", hear me " + this.sound + "!";
        }
    }

    function Cat(){
        this.__proto__ = new Animal();
        this.species = "cat";
        this.sound = "meow";
    }

    function getMessage(){
        var cat = new Cat();
        Cat.prototype.pounce = function() { return "Pounce!"};    //Adding prototype function here

        var Boots = {};
        Boots.__proto__ = new Cat();

        return cat.speak() + '<br>' + Boots.pounce();   //Returning message that Boots.pounce() doesn't exist
    }

})()

When I look at the Cat() object in the debugging window, it shows me that it has no property "pounce", and neither does Boots. What is it I'm doing here that is not working?

I would think that since I added the function to the object's prototype it would be added to the prototype chain and thus inherited.

Thank you very much for your time.

Upvotes: 0

Views: 44

Answers (2)

somethinghere
somethinghere

Reputation: 17358

That's not how you use prototypes, so let's rebuild it in a more standardised fashion:

function Animal(){
    this.species = 'Undefined';
    this.sound = 'silence';
}

Animal.prototype = {
    speak: function(){
        return "I am a " + this.species + ", hear me " + this.sound + "!";
    }
}

function Cat(){
    Animal.apply(this);
    this.species = 'cat';
    this.sound = 'miauw';
}

Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.pounce = function(){ return 'Pounce!'; }

So what have we done here? We start out by creating a function, this is generally known as our constructor. It is mostly used to set the parameters stored. Then we create a prototype object, which contains the methods (and potentially prototyped parameters) for your prototype.

To create a Cat, we create another function and start by applying the first function with the current this object. This allows you to inherit everything the original constructor did when running. It essentially runs the setup for Animal, but on our new Cat. Then you do your custom things, like setting the species to 'cat'. After that, we will make a copy of the pre-existing prototype of Animal, and then append more methods to it.

Important to note is that all this goes into the prototype key, and not __proto__. Keys and variables starting with double underscores are things you should never touch and almost never use yourself - they are considered something the system does for you. The __proto__ you were seeing before is the result of the prototype being applied programatically, and you can see it in the console, but it is not something you should mess with.

Now we can do something like this:

// Ignore this bit, it's a repeat of the code above 
// so I condensed it to a single line. Otherwise, exactly the same.
function Animal(){ this.species = 'Undefined'; this.sound = 'silence';}Animal.prototype = {speak: function(){return "I am a " + this.species + ", hear me " + this.sound + "!";}}; function Cat(){Animal.apply(this);this.species = 'cat';this.sound = 'miauw';}Cat.prototype = Object.create(Animal.prototype);Cat.prototype.pounce = function(){ return 'Pounce!'; }

// Helper function so the code reads clearly, 
// but also writes to the snippet and adds the linebreaks
// and strong tags where necessary
function w(m,s){document.write((s?'<strong>':'')+(m?m:'')+(s?'</strong>':'')+'<br />');};

// Create our variables with the animal and the cat
var justAnAnimal = new Animal();
var pussInBoots = new Cat();

w( 'Speaking', true ); w();
w( 'justAnAnimal.speak()', true );
w( justAnAnimal.speak() );
w( 'pussInBoots.speak()', true );
w( pussInBoots.speak() );

w(); w('Pouncing', true); w();
w( 'justAnAnimal.pounce()', true );
// Use variable.method to check if the method exist
// if it does, then execute it and write that with variable.method()
// Otherwise print a different message so we know whats going on.
w( 
  (justAnAnimal.pounce && justAnAnimal.pounce()) 
  || 'Not every animal pounces (method does not exist for this instance)!' 
);
w( 'pussInBoots.pounce()', true );
w( 
  (pussInBoots.pounce && pussInBoots.pounce()) 
  || 'Not every animal pounces (method does not exist for this instance)!' 
);

w(); w('Checking the type of your animals using instanceof', true); w();
w( 'is justAnAnimal an Animal?', true );
w( justAnAnimal instanceof Animal ? 'Yes' : 'No' );
w( 'is justAnAnimal a Cat?', true );
w( justAnAnimal instanceof Cat ? 'Yes' : 'No' );
w( 'is pussInBoots an Animal?', true );
w( pussInBoots instanceof Animal ? 'Yes' : 'No' );
w( 'is pussInBoots a Cat?', true );
w( pussInBoots instanceof Cat ? 'Yes' : 'No' );
body {
  font-family: 'Monaco', 'Courier MS', courier, monospace;
  font-size: 10px;
}
strong {
  color: #777;
  margin-right: 20px;
  display: inline-block;
  font-weight: normal;
}

I find this makes your code look cleaner as everything is very simple. The constructors, the prototype, everything falls into a pattern that is easy to distinguish, as well as clear to read.

Heres a comprehensive write up over at MDN: https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Upvotes: 1

Dan Prince
Dan Prince

Reputation: 30009

The behaviour for __proto__ hasn't ever been standardized, except for as a legacy feature.

You'll have a better time if you use the Object.create method. It takes the prototype as the first argument and returns an object that uses that prototype.

Your code, if rewritten with Object.create might look more like this.

function Animal() {

}

// you don't need to this, but it shows the prototype chain explicitly
Animal.prototype = Object.create(Object.prototype);

Animal.prototype.speak = function() {
  return "I am a " + this.species + ", hear me " + this.sound + "!";
};

function Cat(){
  this.species = 'cat';
  this.sound = 'meow';
}

Cat.prototype = Object.create(Animal.prototype);

Cat.prototype.pounce = function() {
  return "Pounce";
};

function getMessage() {
  var cat = new Cat();

  // you could dynamically add methods to the prototype, but the
  // code will be faster if you declare the properties on the
  // prototype, as early as possible

  var Boots = new Cat();
  return cat.speak() + '<br>' + Boots.pounce();
}

Upvotes: 1

Related Questions